From 0419eac322dfd22bf31db4db8c7309455230b460 Mon Sep 17 00:00:00 2001 From: "Atishay Jain (atisjai)" Date: Sun, 12 Jan 2025 21:42:32 +0530 Subject: [PATCH 01/20] Make plotly schema strongly typed --- .../bundle-size/DeclarativeChart.fixture.js | 7 + .../DeclarativeChart/DeclarativeChart.tsx | 29 +- .../DeclarativeChart/PlotlySchema.ts | 1950 +++++++++++++++++ .../DeclarativeChart/PlotlySchemaAdapter.ts | 187 +- 4 files changed, 2080 insertions(+), 93 deletions(-) create mode 100644 packages/charts/react-charting/bundle-size/DeclarativeChart.fixture.js create mode 100644 packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchema.ts diff --git a/packages/charts/react-charting/bundle-size/DeclarativeChart.fixture.js b/packages/charts/react-charting/bundle-size/DeclarativeChart.fixture.js new file mode 100644 index 0000000000000..ce50de1aed384 --- /dev/null +++ b/packages/charts/react-charting/bundle-size/DeclarativeChart.fixture.js @@ -0,0 +1,7 @@ +import { DeclarativeChart } from '@fluentui/react-charting'; + +console.log(DeclarativeChart); + +export default { + name: 'DeclarativeChart', +}; diff --git a/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx b/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx index 63f472a281be7..26b022a1837de 100644 --- a/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx +++ b/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx @@ -1,10 +1,11 @@ /* eslint-disable @typescript-eslint/naming-convention */ -/* eslint-disable @typescript-eslint/no-explicit-any */ import * as React from 'react'; import { useTheme } from '@fluentui/react'; import { IRefObject } from '@fluentui/react/lib/Utilities'; import { DonutChart } from '../DonutChart/index'; import { VerticalStackedBarChart } from '../VerticalStackedBarChart/index'; +import { PlotData, PlotlySchema } from './PlotlySchema'; +//import type { Data, Layout } from './PlotlySchema'; import { isArrayOrTypedArray, isDateArray, @@ -87,12 +88,8 @@ export const DeclarativeChart: React.FunctionComponent = DeclarativeChartProps >((props, forwardedRef) => { const { plotlySchema } = sanitizeJson(props.chartSchema); - const { data, layout } = plotlySchema; + const plotlyInput = plotlySchema as PlotlySchema; let { selectedLegends } = plotlySchema; - const xValues = data[0].x; - const isXDate = isDateArray(xValues); - const isXNumber = isNumberArray(xValues); - const isXMonth = isMonthArray(xValues); const colorMap = useColorMapping(); const theme = useTheme(); const isDarkTheme = theme?.isInverted ?? false; @@ -106,7 +103,7 @@ export const DeclarativeChart: React.FunctionComponent = const onActiveLegendsChange = (keys: string[]) => { setActiveLegends(keys); if (props.onSchemaChange) { - props.onSchemaChange({ plotlySchema: { data, layout, selectedLegends: keys } }); + props.onSchemaChange({ plotlySchema: { plotlyInput, selectedLegends: keys } }); } }; @@ -148,7 +145,7 @@ export const DeclarativeChart: React.FunctionComponent = selectedLegends: activeLegends, }; - switch (data[0].type) { + switch (plotlyInput.data[0].type) { case 'pie': return ( = /> ); case 'bar': - const orientation = data[0].orientation; + const orientation = plotlyInput.data[0].orientation; if (orientation === 'h') { return ( = ); } case 'scatter': - const isAreaChart = data.some((series: any) => series.fill === 'tonexty' || series.fill === 'tozeroy'); + const xValues = (plotlyInput.data[0] as PlotData).x; + const isXDate = isDateArray(xValues); + const isXNumber = isNumberArray(xValues); + const isXMonth = isMonthArray(xValues); + const isAreaChart = plotlyInput.data.some((series: PlotData) => series.fill === 'tonexty' || series.fill === 'tozeroy'); const renderChart = (chartProps: any) => { if (isAreaChart) { return ( @@ -216,19 +217,19 @@ export const DeclarativeChart: React.FunctionComponent = }; if (isXDate || isXNumber) { const chartProps = { - ...transformPlotlyJsonToScatterChartProps({ data, layout }, isAreaChart, colorMap, isDarkTheme), + ...transformPlotlyJsonToScatterChartProps(plotlyInput, isAreaChart, colorMap, isDarkTheme), legendProps, componentRef: chartRef, calloutProps: { layerProps: { eventBubblingEnabled: true } }, }; return renderChart(chartProps); } else if (isXMonth) { - const updatedData = data.map((dataPoint: any) => ({ + const updatedData = plotlyInput.data.map((dataPoint: PlotData) => ({ ...dataPoint, x: updateXValues(dataPoint.x), })); const chartProps = { - ...transformPlotlyJsonToScatterChartProps({ data: updatedData, layout }, isAreaChart, colorMap, isDarkTheme), + ...transformPlotlyJsonToScatterChartProps({ data: updatedData, layout: plotlyInput.layout }, isAreaChart, colorMap, isDarkTheme), legendProps, componentRef: chartRef, calloutProps: { layerProps: { eventBubblingEnabled: true } }, @@ -260,7 +261,7 @@ export const DeclarativeChart: React.FunctionComponent = /> ); case 'indicator': - if (data?.[0]?.mode?.includes('gauge')) { + if (plotlyInput.data?.[0]?.mode?.includes('gauge')) { return ( ; + +export interface PieFont { + family: string | string[]; + size: number | number[]; + color: PieColor | PieColors; +} + +export interface PieDataTitle extends Pick { + font: Partial; +} + +export type PieTextPosition = "inside" | "outside" | "auto" | "none"; + +export type PieHoverInfo = + | "all" + | "none" + | "skip" + | "label" + | "text" + | "value" + | "percent" + | "name" + | "label+text" + | "label+value" + | "label+percent" + | "label+name" + | "text+value" + | "text+percent" + | "text+name" + | "value+percent" + | "value+name" + | "percent+name" + | "label+text+value" + | "label+text+percent" + | "label+text+name" + | "label+value+percent" + | "label+value+name" + | "label+percent+name" + | "text+value+percent" + | "text+value+name" + | "text+percent+name" + | "value+percent+name" + | "label+text+value+percent" + | "label+text+value+name" + | "label+text+percent+name" + | "label+value+percent+name" + | "text+value+percent+name"; + +export interface PieDomain { + x: number[]; + y: number[]; + row: number; + column: number; +} + +export interface PieLine { + color: PieColor | PieColors; + width: number | number[]; +} + +export interface PieMarker { + colors: PieColors; + line: Partial; +} + +export interface PieHoverLabel { + bgcolor: PieColor | PieColors; + bordercolor: PieColor | PieColors; + font: PieFont; + align: HoverLabel["align"] | Array; + namelength: number | number[]; +} + +export type PieInsideTextOrientation = "horizontal" | "radial" | "tangential" | "auto"; + +export interface PieData extends + Pick< + PlotData, + | "name" + | "visible" + | "showlegend" + | "legendgroup" + | "opacity" + | "ids" + | "labels" + | "hovertext" + | "automargin" + | "textinfo" + | "direction" + | "hole" + | "rotation" + > +{ + type: "pie"; + title: Partial; + values: Array; + dlabel: number; + label0: number; + pull: number | number[]; + text: Datum | Datum[]; + textposition: PieTextPosition | PieTextPosition[]; + texttemplate: string | string[]; + hoverinfo: PieHoverInfo; + hovertemplate: string | string[]; + meta: number | string; + customdata: Datum[]; + domain: Partial; + marker: Partial; + textfont: PieFont; + hoverlabel: Partial; + insidetextfont: PieFont; + insidetextorientation: PieInsideTextOrientation; + outsidetextfont: PieFont; + scalegroup: string; + sort: boolean; + uirevision: number | string; +} + +export type SankeyColor = string | number; +export type SankeyColors = Array; + +export interface SankeyFont { + family: string | string[]; + size: number | number[]; + color: SankeyColor | SankeyColors; +} + +export interface SankeyDataTitle { + font: Partial; + title: string; +} + +export type SankeyOrientation = "v" | "h"; + +export interface SankeyHoverLabel { + bgcolor: SankeyColor | SankeyColors; + bordercolor: SankeyColor | SankeyColors; + font: SankeyFont; + align: HoverLabel["align"] | Array; + namelength: number | number[]; +} + +export interface SankeyDomain { + row: number; + column: number; + x: number[]; + y: number[]; +} + +export interface SankeyNode { + color: SankeyColor[]; + customdata: Datum[]; + groups: SankeyNode[]; + hoverinfo: "all" | "none" | "skip"; + hoverlabel: Partial; + hovertemplate: string | string[]; + label: Datum[]; + line: Partial<{ + color: SankeyColor; + width: number; + }>; + pad: number; + thickness: number; + x: number[]; + y: number[]; +} + +export interface SankeyColorscale { + cmax: number; + cmin: number; + colorscale: Array<[number, string]>; + label: string; + name: string; + templateitemname: string; +} + +export interface SankeyLink { + arrowlen: number; + color: SankeyColor | SankeyColor[]; + colorscale: Partial; + customdata: Datum[]; + hoverinfo: "all" | "none" | "skip"; + hoverlabel: Partial; + hovertemplate: string | string[]; + hovercolor: SankeyColor | SankeyColor[]; + label: Datum[]; + line: Partial<{ + color: SankeyColor; + width: number; + }>; + source: number[]; + target: number[]; + value: number[]; +} + +export interface SankeyData { + type: "sankey"; + name: string; + orientation: SankeyOrientation; + visible: boolean | "legendonly"; + legend: string; + legendrank: number; + legendgrouptitle: Partial; + legendwidth: number; + ids: string[]; + hoverinfo: string; + meta: number | string; + customdata: Datum[]; + domain: Partial; + node: Partial; + link: Partial; + textfont: Partial; + selectpoints: string | number; + arrangement: "snap" | "perpendicular" | "freeform" | "fixed"; + hoverlabel: Partial; + valueformat: string; + valuesuffix: string; + uirevision: string | number; +} + +export interface Point { + x: number; + y: number; + z: number; +} + +export interface PlotScatterDataPoint { + curveNumber: number; + data: PlotData; + pointIndex: number; + pointNumber: number; + x: number; + xaxis: LayoutAxis; + y: number; + yaxis: LayoutAxis; +} + +export interface PlotDatum { + curveNumber: number; + data: PlotData; + customdata: Datum; + pointIndex: number; + pointNumber: number; + x: Datum; + xaxis: LayoutAxis; + y: Datum; + yaxis: LayoutAxis; + text: string; +} + +export interface PlotCoordinate { + x: number; + y: number; + pointNumber: number; +} + +export interface SelectionRange { + x: number[]; + y: number[]; +} + +export type PlotSelectedData = Partial; + +export interface PlotScene { + center: Point; + eye: Point; + up: Point; +} + +export interface PolarLayout { + domain: Partial; + sector: number[]; + hole: number; + bgcolor: Color; + radialaxis: Partial; + angularaxis: Partial; + gridshape: "circular" | "linear"; + uirevision: string | number; + uid: string; +} + +export interface PlotlySchema { + data: Data[]; + layout?: Partial; + config?: Partial; +} + +// Layout +export interface Layout { + colorway: string[]; + title: + | string + | Partial<{ + text: string; + font: Partial; + xref: "container" | "paper"; + yref: "container" | "paper"; + x: number; + y: number; + xanchor: "auto" | "left" | "center" | "right"; + yanchor: "auto" | "top" | "middle" | "bottom"; + pad: Partial; + }>; + titlefont: Partial; + autosize: boolean; + showlegend: boolean; + paper_bgcolor: Color; + plot_bgcolor: Color; + separators: string; + hidesources: boolean; + xaxis: Partial; + xaxis2: Partial; + xaxis3: Partial; + xaxis4: Partial; + xaxis5: Partial; + xaxis6: Partial; + xaxis7: Partial; + xaxis8: Partial; + xaxis9: Partial; + yaxis: Partial; + yaxis2: Partial; + yaxis3: Partial; + yaxis4: Partial; + yaxis5: Partial; + yaxis6: Partial; + yaxis7: Partial; + yaxis8: Partial; + yaxis9: Partial; + margin: Partial; + height: number; + width: number; + hovermode: "closest" | "x" | "y" | "x unified" | "y unified" | false; + hoverdistance: number; + hoverlabel: Partial; + calendar: Calendar; + "xaxis.range": [Datum, Datum]; + "xaxis.range[0]": Datum; + "xaxis.range[1]": Datum; + "yaxis.range": [Datum, Datum]; + "yaxis.range[0]": Datum; + "yaxis.range[1]": Datum; + "yaxis.type": AxisType; + "xaxis.type": AxisType; + "xaxis.autorange": boolean; + "yaxis.autorange": boolean; + "xaxis.title": string; + "yaxis.title": string; + ternary: any; + geo: any; + mapbox: any; + subplot: string; + radialaxis: Partial; + angularaxis: {}; + dragmode: + | "zoom" + | "pan" + | "select" + | "lasso" + | "drawclosedpath" + | "drawopenpath" + | "drawline" + | "drawrect" + | "drawcircle" + | "orbit" + | "turntable" + | false; + orientation: number; + annotations: Array>; + shapes: Array>; + legend: Partial; + font: Partial; + barmode: "stack" | "group" | "overlay" | "relative"; + barnorm: "" | "fraction" | "percent"; + bargap: number; + bargroupgap: number; + boxmode: "group" | "overlay"; + selectdirection: "h" | "v" | "d" | "any"; + hiddenlabels: string[]; + grid: Partial<{ + rows: number; + roworder: "top to bottom" | "bottom to top"; + columns: number; + subplots: string[]; + xaxes: string[]; + yaxes: string[]; + pattern: "independent" | "coupled"; + xgap: number; + ygap: number; + domain: Partial<{ + x: number[]; + y: number[]; + }>; + xside: "bottom" | "bottom plot" | "top plot" | "top"; + yside: "left" | "left plot" | "right plot" | "right"; + }>; + polar: Partial; + polar2: Partial; + polar3: Partial; + polar4: Partial; + polar5: Partial; + polar6: Partial; + polar7: Partial; + polar8: Partial; + polar9: Partial; + template: Template; + clickmode: "event" | "select" | "event+select" | "none"; + uirevision: number | string; + uid: string; + datarevision: number | string; + editrevision: number | string; + selectionrevision: number | string; +} + +export interface Legend extends Label { + borderwidth: number; + groupclick: "toggleitem" | "togglegroup"; + grouptitlefont: Partial; + itemclick: "toggle" | "toggleothers" | false; + itemdoubleclick: "toggle" | "toggleothers" | false; + itemsizing: "trace" | "constant"; + itemwidth: number; + orientation: "v" | "h"; + title: Partial; + tracegroupgap: number; + traceorder: "grouped" | "normal" | "reversed" | "reversed+grouped"; + uirevision: number | string; + uid: string; + valign: "top" | "middle" | "bottom"; + x: number; + xanchor: "auto" | "left" | "center" | "right"; + xref: "container" | "paper"; + y: number; + yanchor: "auto" | "top" | "middle" | "bottom"; + yref: "container" | "paper"; +} + +export type AxisType = "-" | "linear" | "log" | "date" | "category" | "multicategory"; + +export type DTickValue = number | string; + +export interface TickFormatStop { + /** + * Determines whether or not this stop is used. If `false`, + * this stop is ignored even within its `dtickrange`. + */ + enabled: boolean; + /** + * Range [`min`, `max`], where `min`, `max` - dtick values + * which describe some zoom level, it is possible to omit `min` or `max` + * value by passing `null` + */ + dtickrange: [DTickValue | null, DTickValue | null]; + /** + * dtickformat for described zoom level, the same as `tickformat` + */ + value: string; + /** + * When used in a template, named items are created in the output figure + * in addition to any items the figure already has in this array. + * You can modify these items in the output figure by making + * your own item with `templateitemname` matching this `name` + * alongside your modifications (including `visible: false` or `enabled: false` to hide it). + * Has no effect outside of a template. + */ + name: string; + /** + * Used to refer to a named item in this array in the template. + * Named items from the template will be created even without + * a matching item in the input figure, but you can modify one by + * making an item with `templateitemname` matching its `name`, + * alongside your modifications (including `visible: false` or `enabled: false` to hide it). + * If there is no template or no matching item, this item will be hidden + * unless you explicitly show it with `visible: true`. + */ + templateitemname: string; +} + +export interface AutoRangeOptions { + clipmax: DTickValue; + clipmin: DTickValue; + include: DTickValue; + maxallowed: DTickValue; + minallowed: DTickValue; +} + +export interface MinorAxisLayout { + dtick: DTickValue; + gridcolor: Color; + griddash: Dash; + gridwidth: number; + nticks: number; + showgrid: boolean; + tick0: DTickValue; + tickcolor: Color; + ticklen: number; + tickmode: "auto" | "linear" | "array"; + ticks: "outside" | "inside" | ""; + tickvals: any[]; + tickwidth: number; +} + +export interface RangeBreak { + bounds: any[]; + dvalue: number; + enabled: boolean; + name: string; + pattern: "day of week" | "hour" | ""; + templateitemname: string; + values: any[]; +} + +export interface Axis { + /** + * A single toggle to hide the axis while preserving interaction like dragging. + * Default is true when a cheater plot is present on the axis, otherwise + * false + */ + visible: boolean; + /** + * Sets default for all colors associated with this axis + * all at once: line, font, tick, and grid colors. + * Grid color is lightened by blending this with the plot background + * Individual pieces can override this. + */ + color: Color; + title: string | Partial; + /** + * Former `titlefont` is now the sub-attribute `font` of `title`. + * To customize title font properties, please use `title.font` now. + */ + titlefont: Partial; + type: AxisType; + autorange: true | false | "reversed" | "min reversed" | "max reversed" | "min" | "max"; + autorangeoptions: Partial; + /** + * 'If *normal*, the range is computed in relation to the extrema + * of the input data. + * If *tozero*`, the range extends to 0, + * regardless of the input data + * If *nonnegative*, the range is non-negative, + * regardless of the input data. + * Applies only to linear axes. + */ + rangemode: "normal" | "tozero" | "nonnegative"; + range: any[]; + /** + * Determines whether or not this axis is zoom-able. + * If true, then zoom is disabled. + */ + fixedrange: boolean; + + /** + * Ticks + */ + tickmode: "auto" | "linear" | "array"; + nticks: number; + tick0: number | string; + dtick: DTickValue; + tickvals: any[]; + ticktext: string[]; + ticks: "outside" | "inside" | ""; + mirror: true | "ticks" | false | "all" | "allticks"; + ticklen: number; + tickwidth: number; + tickcolor: Color; + showticklabels: boolean; + showspikes: boolean; + spikecolor: Color; + spikethickness: number; + /** + * Specifies the ordering logic for the case of categorical variables. + * By default, plotly uses *trace*, which specifies the order that is present in the data supplied. + * Set `categoryorder` to *category ascending* or *category descending* if order should be determined by + * the alphanumerical order of the category names. + * Set `categoryorder` to *array* to derive the ordering from the attribute `categoryarray`. If a category + * is not found in the `categoryarray` array, the sorting behavior for that attribute will be identical to + * the *trace* mode. The unspecified categories will follow the categories in `categoryarray`. + * Set `categoryorder` to *total ascending* or *total descending* if order should be determined by the + * numerical order of the values. + * Similarly, the order can be determined by the min, max, sum, mean or median of all the values. + */ + categoryorder: + | "trace" + | "category ascending" + | "category descending" + | "array" + | "total ascending" + | "total descending" + | "min ascending" + | "min descending" + | "max ascending" + | "max descending" + | "sum ascending" + | "sum descending" + | "mean ascending" + | "mean descending" + | "median ascending" + | "median descending"; + categoryarray: any[]; + tickfont: Partial; + tickangle: "auto" | number; + tickprefix: string; + /** + * If `all`, all tick labels are displayed with a prefix. + * If `first`, only the first tick is displayed with a prefix. + * If `last`, only the last tick is displayed with a suffix. + * If `none`, tick prefixes are hidden. + */ + showtickprefix: "all" | "first" | "last" | "none"; + /** + * Sets a tick label suffix. + */ + ticksuffix: string; + /** + * Same as `showtickprefix` but for tick suffixes. + */ + showticksuffix: "all" | "first" | "last" | "none"; + /** + * If `all`, all exponents are shown besides their significands. + * If `first`, only the exponent of the first tick is shown. + * If `last`, only the exponent of the last tick is shown. + * If `none`, no exponents appear. + */ + showexponent: "all" | "first" | "last" | "none"; + /** + * Determines a formatting rule for the tick exponents. + * For example, consider the number 1,000,000,000. + * If `none`, it appears as *1,000,000,000*. + * If `e`, *1e+9*. + * If `E`, *1E+9*. + * If `power`, *1x10^9* (with 9 in a super script). + * If `SI`, *1G*. + * If `B`, *1B*. + */ + exponentformat: "none" | "e" | "E" | "power" | "SI" | "B"; + /** + * Hide SI prefix for 10^n if |n| is below this number. This only has an effect when `tickformat` is "SI" or "B". + */ + minexponent: number; + /** + * 'If `true`, even 4-digit integers are separated + */ + separatethousands: boolean; + /** + * Sets the tick label formatting rule using d3 formatting mini-languages + * which are very similar to those in Python. + * For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format + * And for dates see: https://github.com/d3/d3-3.x-api-reference/blob/master/Time-Formatting.md#format + * We add one item to d3's date formatter: `%{n}f` for fractional seconds with n digits. + * For example, `"2016-10-13 09:15:23.456"` with tickformat `"%H~%M~%S.%2f"` would display `"09~15~23.46"` + */ + tickformat: string; + /** + * Sets the hover text formatting rule using d3 formatting mini-languages + * which are very similar to those in Python. + * For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format + * And for dates see: https://github.com/d3/d3-3.x-api-reference/blob/master/Time-Formatting.md#format + * We add one item to d3's date formatter: `%{n}f` for fractional seconds with n digits. + * For example, `"2016-10-13 09:15:23.456"` with tickformat `"%H~%M~%S.%2f"` would display "09~15~23.46" + */ + hoverformat: string; + calendar: Calendar; + /** + * Array of `Partial` objects. + */ + tickformatstops: Array>; + spikedash: string; + /** + * Determines the drawing mode for the spike line. + * If `toaxis`, the line is drawn from the data point to the axis the + * series is plotted on. + * If `across`, the line is drawn across the entire plot area, and + * supercedes *toaxis*. + * If `marker`, then a marker dot is drawn on the axis the series is + * plotted on + */ + spikemode: + | "toaxis" + | "across" + | "marker" + | "toaxis+across" + | "toaxis+across+marker" + | "across+marker" + | "toaxis+marker"; + /** + * Determines whether spikelines are stuck to the cursor or to the closest datapoints. + */ + spikesnap: "data" | "cursor" | "hovered data"; + + /** + * Lines and Grids + */ + + /** + * Determines whether or not a line bounding this axis is drawn. + */ + showline: boolean; + /** + * Sets the axis line color + */ + linecolor: Color; + /** + * Sets the width (in px) of the axis line. + */ + linewidth: number; + /** + * Determines whether or not grid lines are drawn. + * If `true`, the grid lines are drawn at every tick mark. + */ + showgrid: boolean; + /** + * Sets the color of the grid lines. + */ + gridcolor: Color; + /** + * Sets the width (in px) of the grid lines. + */ + gridwidth: number; + /** + * Determines whether or not a line is drawn at along the 0 value + * of this axis. + * If `true`, the zero line is drawn on top of the grid lines. + */ + zeroline: boolean; + /** + * Sets the line color of the zero line. + */ + zerolinecolor: Color; + /** + * Sets the width (in px) of the zero line. + */ + zerolinewidth: number; + /** + * Determines whether or not a dividers are drawn + * between the category levels of this axis. + * Only has an effect on *multicategory* axes. + */ + showdividers: boolean; + /** + * Sets the color of the dividers + * Only has an effect on *multicategory* axes. + */ + dividercolor: Color; + /** + * Sets the width (in px) of the dividers + * Only has an effect on *multicategory* axes. + */ + dividerwidth: number; + + autotypenumbers: "convert types" | "strict"; + labelalias: DTickValue; + maxallowed: DTickValue; + minallowed: DTickValue; +} + +export type Calendar = + | "gregorian" + | "chinese" + | "coptic" + | "discworld" + | "ethiopian" + | "hebrew" + | "islamic" + | "julian" + | "mayan" + | "nanakshahi" + | "nepali" + | "persian" + | "jalali" + | "taiwan" + | "thai" + | "ummalqura"; + +// regex from documentation: "/^x([2-9]|[1-9][0-9]+)?( domain)?$/" | "/^y([2-9]|[1-9][0-9]+)?( domain)?$/" +// regex allows for an unlimited amount of digits for the 'axis number', but the following typescript definition is limited to two digits +type xYAxisNames = `${ + | "" + | `${2 | 3 | 4 | 5 | 6 | 7 | 8 | 9}` + | `${1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9}${0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9}`}${"" | " domain"}`; + +export type XAxisName = `x${xYAxisNames}`; +export type YAxisName = `y${xYAxisNames}`; + +export type AxisName = XAxisName | YAxisName; + +export interface LayoutAxis extends Axis { + fixedrange: boolean; + scaleanchor: AxisName; + scaleratio: number; + constrain: "range" | "domain"; + constraintoward: "left" | "center" | "right" | "top" | "middle" | "bottom"; + anchor: "free" | AxisName; + side: "top" | "bottom" | "left" | "right" | "clockwise" | "counterclockwise"; + overlaying: "free" | AxisName; + layer: "above traces" | "below traces"; + domain: number[]; + position: number; + rotation: number; + direction: "counterclockwise" | "clockwise"; + rangeslider: Partial; + rangeselector: Partial; + automargin: boolean; + autotick: boolean; + angle: any; + griddash: Dash; + l2p: (v: Datum) => number; + + autotickangles: number[]; + insiderange: any[]; + matches: AxisName; + minor: Partial; + rangebreaks: Array>; + ticklabelmode: "instant" | "period"; + ticklabeloverflow: "allow" | "hide past div" | "hide past domain"; + ticklabelposition: + | "outside" + | "inside" + | "outside top" + | "inside top" + | "outside left" + | "inside left" + | "outside right" + | "inside right" + | "outside bottom" + | "inside bottom"; + ticklabelstep: number; + tickson: "labels" | "boundaries"; + uirevision: DTickValue; +} + +export interface SceneAxis extends Axis { + spikesides: boolean; + showbackground: boolean; + backgroundcolor: Color; + showaxeslabels: boolean; +} + +export interface ShapeLine { + color: string; + width: number; + dash: Dash; +} + +export interface ShapeLabel { + font: Partial; + padding: number; + text: string; + textangle: "auto" | number; + textposition: + | "top left" + | "top center" + | "top right" + | "middle left" + | "middle center" + | "middle right" + | "bottom left" + | "bottom center" + | "bottom right" + | "start" + | "middle" + | "end"; + texttemplate: string; + xanchor: "auto" | "left" | "center" | "right"; + yanchor: "top" | "middle" | "bottom"; +} + +export interface Shape { + visible: boolean | "legendonly"; + layer: "below" | "above"; + type: "rect" | "circle" | "line" | "path"; + path: string; + xref: "paper" | XAxisName; + xsizemode: "scaled" | "pixel"; + xanchor: number | string; + yref: "paper" | YAxisName; + ysizemode: "scaled" | "pixel"; + yanchor: number | string; + x0: Datum; + y0: Datum; + x1: Datum; + y1: Datum; + fillcolor: string; + name: string; + templateitemname: string; + opacity: number; + line: Partial; + label: Partial; + showlegend: boolean; + legendgroup: string; + legendgrouptitle: { + text: string; + font?: Partial; + }; + legendrank: number; +} + +export interface Margin { + t: number; + b: number; + l: number; + r: number; + pad: number; +} + +export interface Icon { + height?: number | undefined; + width?: number | undefined; + ascent?: number | undefined; + descent?: number | undefined; + name?: string | undefined; + path?: string | undefined; + svg?: string | undefined; + transform?: string | undefined; +} + +export interface GaugeLine { + color: Color; + width: number; +} +export interface Threshold { + line: Partial; + value: number; + thickness: number; +} + +export interface GaugeBar { + color: Color; + line: Partial; + thickness: number; +} +export interface Gauge { + shape: "angular" | "bullet"; + bar: Partial; + bgcolor: Color; + bordercolor: Color; + borderwidth: number; + axis: Partial; + steps: Array<{ range: number[]; color: Color }>; + threshold: Partial; +} + +export interface Delta { + reference: number; + position: "top" | "bottom" | "left" | "right"; + relative: boolean; + valueformat: string; + increasing: { + symbol: string; + color: Color; + }; + decreasing: { + symbol: string; + color: Color; + }; +} + +export interface DataTitle { + text: string; + font: Partial; + standoff: number; + position: + | "top left" + | "top center" + | "top right" + | "middle center" + | "bottom left" + | "bottom center" + | "bottom right"; +} + +export interface PlotNumber { + valueformat: string; + font: Partial; + prefix: string; + suffix: string; +} + +export interface Template { + data?: { [type in PlotType]?: Array> } | undefined; + layout?: Partial | undefined; +} + +// Data + +export type Datum = string | number | Date | null; +export type TypedArray = + | Int8Array + | Uint8Array + | Int16Array + | Uint16Array + | Int32Array + | Uint32Array + | Uint8ClampedArray + | Float32Array + | Float64Array; + +export interface ErrorOptions { + visible: boolean; + symmetric: boolean; + color: Color; + thickness: number; + width: number; + opacity: number; +} + +export type ErrorBar = + & Partial + & ( + | { + type: "constant" | "percent"; + value: number; + valueminus?: number | undefined; + } + | { + type: "data"; + array: Datum[]; + arrayminus?: Datum[] | undefined; + } + ); + +export type Dash = "solid" | "dot" | "dash" | "longdash" | "dashdot" | "longdashdot"; +export type PlotType = + | "bar" + | "barpolar" + | "box" + | "candlestick" + | "carpet" + | "choropleth" + | "choroplethmapbox" + | "cone" + | "contour" + | "contourcarpet" + | "densitymapbox" + | "funnel" + | "funnelarea" + | "heatmap" + | "heatmapgl" + | "histogram" + | "histogram2d" + | "histogram2dcontour" + | "image" + | "indicator" + | "isosurface" + | "mesh3d" + | "ohlc" + | "parcats" + | "parcoords" + | "pie" + | "pointcloud" + | "sankey" + | "scatter" + | "scatter3d" + | "scattercarpet" + | "scattergeo" + | "scattergl" + | "scattermapbox" + | "scatterpolar" + | "scatterpolargl" + | "scatterternary" + | "splom" + | "streamtube" + | "sunburst" + | "surface" + | "table" + | "treemap" + | "violin" + | "volume" + | "waterfall"; + +export type Data = + | Partial + | Partial + | Partial; + +export type Color = + | string + | number + | Array + | Array>; +export type ColorScale = string | string[] | Array<[number, string]>; +export type DataTransform = Partial; +export type ScatterData = PlotData; + +// Bar Scatter +export interface PlotData { + type: PlotType; + x: Datum[] | Datum[][] | TypedArray; + y: Datum[] | Datum[][] | TypedArray; + z: Datum[] | Datum[][] | Datum[][][] | TypedArray; + i: TypedArray; + j: TypedArray; + k: TypedArray; + xy: Float32Array; + error_x: ErrorBar; + error_y: ErrorBar; + xaxis: string; + yaxis: string; + text: string | string[]; + lat: Datum[]; + lon: Datum[]; + line: Partial; + "line.color": Color; + "line.width": number; + "line.dash": Dash; + "line.shape": "linear" | "spline" | "hv" | "vh" | "hvh" | "vhv"; + "line.smoothing": number; + "line.simplify": boolean; + marker: Partial; + "marker.symbol": MarkerSymbol | MarkerSymbol[]; + "marker.color": Color; + "marker.colorscale": ColorScale | ColorScale[]; + "marker.opacity": number | number[]; + "marker.size": number | number[] | number[][]; + "marker.maxdisplayed": number; + "marker.sizeref": number; + "marker.sizemax": number; + "marker.sizemin": number; + "marker.sizemode": "diameter" | "area"; + "marker.showscale": boolean; + "marker.line": Partial; + "marker.line.color": Color; + "marker.line.colorscale": ColorScale | ColorScale[]; + "marker.colorbar": {}; // TODO + "marker.pad.t": number; + "marker.pad.b": number; + "marker.pad.l": number; + "marker.pad.r": number; + mode: + | "lines" + | "markers" + | "text" + | "lines+markers" + | "text+markers" + | "text+lines" + | "text+lines+markers" + | "none" + | "gauge" + | "number" + | "delta" + | "number+delta" + | "gauge+number" + | "gauge+number+delta" + | "gauge+delta"; + histfunc: "count" | "sum" | "avg" | "min" | "max"; + histnorm: "" | "percent" | "probability" | "density" | "probability density"; + hoveron: "points" | "fills"; + hoverinfo: + | "all" + | "name" + | "none" + | "skip" + | "text" + | "x" + | "x+text" + | "x+name" + | "x+y" + | "x+y+text" + | "x+y+name" + | "x+y+z" + | "x+y+z+text" + | "x+y+z+name" + | "y" + | "y+name" + | "y+x" + | "y+text" + | "y+x+text" + | "y+x+name" + | "y+z" + | "y+z+text" + | "y+z+name" + | "y+x+z" + | "y+x+z+text" + | "y+x+z+name" + | "z" + | "z+x" + | "z+x+text" + | "z+x+name" + | "z+y+x" + | "z+y+x+text" + | "z+y+x+name" + | "z+x+y" + | "z+x+y+text" + | "z+x+y+name"; + hoverlabel: Partial; + hovertemplate: string | string[]; + hovertext: string | string[]; + hoverongaps: boolean; + xhoverformat: string; + yhoverformat: string; + zhoverformat: string; + texttemplate: string | string[]; + textinfo: + | "label" + | "label+text" + | "label+value" + | "label+percent" + | "label+text+value" + | "label+text+percent" + | "label+value+percent" + | "text" + | "text+value" + | "text+percent" + | "text+value+percent" + | "value" + | "value+percent" + | "percent" + | "none"; + textposition: + | "top left" + | "top center" + | "top right" + | "middle left" + | "middle center" + | "middle right" + | "bottom left" + | "bottom center" + | "bottom right" + | "inside" + | "outside" + | "auto" + | "none"; + textfont: Partial; + textangle: "auto" | number; + insidetextanchor: "end" | "middle" | "start"; + constraintext: "inside" | "outside" | "both" | "none"; + fill: "none" | "tozeroy" | "tozerox" | "tonexty" | "tonextx" | "toself" | "tonext"; + fillcolor: string; + fillpattern: Partial; + showlegend: boolean; + legendgroup: string; + legendgrouptitle: { + text: string; + font?: Partial; + }; + legendrank: number; + parents: string[]; + name: string; + stackgroup: string; + groupnorm: "" | "fraction" | "percent"; + stackgaps: "infer zero" | "interpolate"; + connectgaps: boolean; + visible: boolean | "legendonly"; + delta: Partial; + gauge: Partial; + number: Partial; + transforms: DataTransform[]; + orientation: "v" | "h"; + width: number | number[]; + boxmean: boolean | "sd"; + boxpoints: "all" | "outliers" | "suspectedoutliers" | false; + jitter: number; + pointpos: number; + opacity: number; + showscale: boolean; + colorscale: ColorScale; + zsmooth: "fast" | "best" | false; + zmin: number; + zmax: number; + ygap: number; + xgap: number; + transpose: boolean; + autobinx: boolean; + xbins: { + start: number | string; + end: number | string; + size: number | string; + }; + value: number; + values: Datum[]; + labels: Datum[]; + direction: "clockwise" | "counterclockwise"; + hole: number; + rotation: number; + theta: Datum[]; + r: Datum[]; + customdata: Datum[] | Datum[][]; + selectedpoints: Datum[]; + domain: Partial<{ + row: number; + column: number; + x: number[]; + y: number[]; + }>; + title: Partial; + branchvalues: "total" | "remainder"; + ids: string[]; + level: string; + cliponaxis: boolean; + automargin: boolean; + locationmode: "ISO-3" | "USA-states" | "country names" | "geojson-id"; + locations: Datum[]; + reversescale: boolean; + colorbar: Partial; + offset: number | number[]; + contours: Partial<{ + coloring: "fill" | "heatmap" | "lines" | "none"; + end: number; + labelfont: Partial; + labelformat: string; + operation: "=" | "<" | ">=" | ">" | "<=" | "[]" | "()" | "[)" | "(]" | "][" | ")(" | "](" | ")["; + showlabels: boolean; + showlines: boolean; + size: number; + start: number; + type: "levels" | "constraint"; + value: number | [lowerBound: number, upperBound: number]; + }>; + autocontour: boolean; + ncontours: number; + uirevision: string | number; + uid: string; +} + +/** + * These interfaces are based on attribute descriptions in + * https://github.com/plotly/plotly.js/tree/9d6144304308fc3007f0facf2535d38ea3e9b26c/src/transforms + */ +export interface TransformStyle { + target: number | string | number[] | string[]; + value: Partial; +} + +export interface TransformAggregation { + target: string; + func?: + | "count" + | "sum" + | "avg" + | "median" + | "mode" + | "rms" + | "stddev" + | "min" + | "max" + | "first" + | "last" + | undefined; + funcmode?: "sample" | "population" | undefined; + enabled?: boolean | undefined; +} + +export interface Transform { + type: "aggregate" | "filter" | "groupby" | "sort"; + enabled: boolean; + target: number | string | number[] | string[]; + operation: string; + aggregations: TransformAggregation[]; + preservegaps: boolean; + groups: string | number[] | string[]; + nameformat: string; + styles: TransformStyle[]; + value: any; + order: "ascending" | "descending"; +} + +export interface ColorBar { + thicknessmode: "fraction" | "pixels"; + thickness: number; + lenmode: "fraction" | "pixels"; + len: number; + x: number; + xanchor: "left" | "center" | "right"; + xpad: number; + y: number; + yanchor: "top" | "middle" | "bottom"; + ypad: number; + outlinecolor: Color; + outlinewidth: number; + bordercolor: Color; + borderwidth: Color; + bgcolor: Color; + tickmode: "auto" | "linear" | "array"; + nticks: number; + tick0: number | string; + dtick: DTickValue; + tickvals: Datum[] | Datum[][] | Datum[][][] | TypedArray; + ticktext: Datum[] | Datum[][] | Datum[][][] | TypedArray; + ticks: "outside" | "inside" | ""; + ticklen: number; + tickwidth: number; + tickcolor: Color; + showticklabels: boolean; + tickfont: Font; + tickangle: "auto" | number; + tickformat: string; + tickformatstops: Array>; + tickprefix: string; + showtickprefix: "all" | "first" | "last" | "none"; + ticksuffix: string; + showticksuffix: "all" | "first" | "last" | "none"; + separatethousands: boolean; + exponentformat: "none" | "e" | "E" | "power" | "SI" | "B"; + showexponent: "all" | "first" | "last" | "none"; + minexponent: number; + title: string; + titlefont: Font; + titleside: "right" | "top" | "bottom"; + tickvalssrc: any; + ticktextsrc: any; +} + +export type MarkerSymbol = string | number | Array; + +/** + * Any combination of "x", "y", "z", "text", "name" joined with a "+" OR "all" or "none" or "skip". + * examples: "x", "y", "x+y", "x+y+z", "all" + * default: "all" + */ +export interface PlotMarker { + symbol: MarkerSymbol; + color?: Color | Color[] | undefined; + colors?: Color[] | undefined; + colorscale?: ColorScale | undefined; + cauto?: boolean | undefined; + cmax?: number | undefined; + cmin?: number | undefined; + autocolorscale?: boolean | undefined; + reversescale?: boolean | undefined; + opacity: number | number[]; + size: number | number[]; + maxdisplayed?: number | undefined; + sizeref?: number | undefined; + sizemax?: number | undefined; + sizemin?: number | undefined; + sizemode?: "diameter" | "area" | undefined; + showscale?: boolean | undefined; + line: Partial; + pad?: Partial | undefined; + width?: number | undefined; + colorbar?: Partial | undefined; + gradient?: + | { + type: "radial" | "horizontal" | "vertical" | "none"; + color: Color; + typesrc: any; + colorsrc: any; + } + | undefined; + pattern?: Partial; +} + +export type ScatterMarker = PlotMarker; + +export interface ScatterMarkerLine { + width: number | number[]; + color: Color; + cauto?: boolean | undefined; + cmax?: number | undefined; + cmin?: number | undefined; + cmid?: number | undefined; + colorscale?: ColorScale | undefined; + autocolorscale?: boolean | undefined; + reversescale?: boolean | undefined; + coloraxis?: string | undefined; +} + +export interface ScatterLine { + color: Color; + width: number; + dash: Dash; + shape: "linear" | "spline" | "hv" | "vh" | "hvh" | "vhv"; + smoothing: number; + simplify: boolean; +} + +export interface Font { + color: Color; + /** + * HTML font family - the typeface that will be applied by the web browser. + * The web browser will only be able to apply a font if it is available on the system + * which it operates. Provide multiple font families, separated by commas, to indicate + * the preference in which to apply fonts if they aren't available on the system. + * The plotly service (at https://plot.ly or on-premise) generates images on a server, + * where only a select number of fonts are installed and supported. + * These include *Arial*, *Balto*, *Courier New*, *Droid Sans*, *Droid Serif*, + * *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, + * *PT Sans Narrow*, *Raleway*, *Times New Roman*. + * @default "Arial, sans-serif" + */ + family: string; + /** + * Sets the shape and color of the shadow behind text. "auto" places minimal shadow and applies contrast text font color. See https://developer.mozilla.org/en-US/docs/Web/CSS/text-shadow for additional options. + * @default "none" + */ + shadow: string; + /** + * number greater than or equal to 1 + * @default 13 + */ + size: number; + /** + * Sets the weight (or boldness) of the font. + * number between or equal to 1 and 1000 + * @default normal + */ + weight: number; +} + +export interface Config { + /** + * Determines whether math should be typeset or not, + * when MathJax (either v2 or v3) is present on the page. + */ + typesetMath: boolean; + + /** DO autosize once regardless of layout.autosize (use default width or height values otherwise) */ + autosizable: boolean; + + /** set the length of the undo/redo queue */ + queueLength: number; + + /** if we DO autosize, do we fill the container or the screen? */ + fillFrame: boolean; + + /** if we DO autosize, set the frame margins in percents of plot size */ + frameMargins: number; + + /** Set global transform to be applied to all traces with no specification needed */ + globalTransforms: any[]; + + /** Which localization should we use? Should be a string like 'en' or 'en-US' */ + locale: string; + + /** + * Localization definitions + * Locales can be provided either here (specific to one chart) or globally + * by registering them as modules. + * Should be an object of objects {locale: {dictionary: {...}, format: {...}}} + * { + * da: { + * dictionary: {'Reset axes': 'Nulstil aksler', ...}, + * format: {months: [...], shortMonths: [...]} + * }, + * ... + * } + * All parts are optional. When looking for translation or format fields, we + * look first for an exact match in a config locale, then in a registered + * module. If those fail, we strip off any regionalization ('en-US' -> 'en') + * and try each (config, registry) again. The final fallback for translation + * is untranslated (which is US English) and for formats is the base English + * (the only consequence being the last fallback date format %x is DD/MM/YYYY + * instead of MM/DD/YYYY). Currently `grouping` and `currency` are ignored + * for our automatic number formatting, but can be used in custom formats. + */ + locales: {}; + + /** Make the chart responsive to window size */ + responsive: boolean; +} + +// Components + +export interface RangeSlider { + visible: boolean; + thickness: number; + range: [Datum, Datum]; + borderwidth: number; + bordercolor: string; + bgcolor: string; +} + +export interface RangeSelectorButton { + step: "second" | "minute" | "hour" | "day" | "month" | "year" | "all"; + stepmode: "backward" | "todate"; + count: number; + label: string; +} + +export interface RangeSelector extends Label { + buttons: Array>; + visible: boolean; + x: number; + xanchor: "auto" | "left" | "center" | "right"; + y: number; + yanchor: "auto" | "top" | "middle" | "bottom"; + activecolor: string; + borderwidth: number; +} + +export interface Label { + /** Sets the background color of all hover labels on graph. */ + bgcolor: string; + + /** Sets the border color of all hover labels on graph. */ + bordercolor: string; + + /** Sets the default hover label font used by all traces on the graph. */ + font: Partial; +} + +export interface LegendTitle { + font: Partial; + side: "top" | "left" | "top left" | "top center" | "top right"; + text: string; +} + +export interface HoverLabel extends Label { + /** + * Sets the horizontal alignment of the text content within hover label box. + * @default "auto" + */ + align: "left" | "right" | "auto"; + + /** + * Sets the default length (in number of characters) of the trace name + * in the hover labels for all traces. + * -1 shows the whole name regardless of length. + * @default 15 + */ + namelength: number; +} + +export interface Annotations extends Label { + /** Determines whether or not this annotation is visible. */ + visible: boolean; + + /** + * Sets the text associated with this annotation. + * Plotly uses a subset of HTML tags to do things like + * newline (
), bold (), italics (), + * hyperlinks (). Tags , , + * are also supported. + */ + text: string; + + /** Sets the angle at which the `text` is drawn with respect to the horizontal. */ + textangle: string; + + /** + * Sets an explicit width for the text box. null (default) lets the + * text set the box width. Wider text will be clipped. + * There is no automatic wrapping; use
to start a new line. + */ + width: number; + + /** + * Sets an explicit height for the text box. null (default) lets the + * text set the box height. Taller text will be clipped. + */ + height: number; + + /** Sets the opacity of the annotation (text + arrow). */ + opacity: number; + + /** + * Sets the horizontal alignment of the `text` within the box. + * Has an effect only if `text` spans more two or more lines + * (i.e. `text` contains one or more
HTML tags) or if an + * explicit width is set to override the text width. + */ + align: "left" | "center" | "right"; + + /** + * Sets the vertical alignment of the `text` within the box. + * Has an effect only if an explicit height is set to override the text height. + */ + valign: "top" | "middle" | "bottom"; + + /** Sets the padding (in px) between the `text` and the enclosing border. */ + borderpad: number; + + /** Sets the width (in px) of the border enclosing the annotation `text`. */ + borderwidth: number; + + /** + * Determines whether or not the annotation is drawn with an arrow. + * If *true*, `text` is placed near the arrow's tail. + * If *false*, `text` lines up with the `x` and `y` provided. + */ + showarrow: boolean; + + /** Sets the color of the annotation arrow. */ + arrowcolor: string; + + /** Sets the end annotation arrow head style. */ + arrowhead: number; + + /** Sets the start annotation arrow head style. */ + startarrowhead: number; + + /** Sets the annotation arrow head position. */ + arrowside: "end" | "start"; + + /** + * Sets the size of the end annotation arrow head, relative to `arrowwidth`. + * A value of 1 (default) gives a head about 3x as wide as the line. + */ + arrowsize: number; + + /** + * Sets the size of the start annotation arrow head, relative to `arrowwidth`. + * A value of 1 (default) gives a head about 3x as wide as the line. + */ + startarrowsize: number; + + /** Sets the width (in px) of annotation arrow line. */ + arrowwidth: number; + + /** + * Sets a distance, in pixels, to move the end arrowhead away from the + * position it is pointing at, for example to point at the edge of + * a marker independent of zoom. Note that this shortens the arrow + * from the `ax` / `ay` vector, in contrast to `xshift` / `yshift` + * which moves everything by this amount. + */ + standoff: number; + + /** + * Sets a distance, in pixels, to move the start arrowhead away from the + * position it is pointing at, for example to point at the edge of + * a marker independent of zoom. Note that this shortens the arrow + * from the `ax` / `ay` vector, in contrast to `xshift` / `yshift` + * which moves everything by this amount. + */ + startstandoff: number; + + /** + * Sets the x component of the arrow tail about the arrow head. + * If `axref` is `pixel`, a positive (negative) + * component corresponds to an arrow pointing + * from right to left (left to right). + * If `axref` is an axis, this is an absolute value on that axis, + * like `x`, NOT a relative value. + */ + ax: number; + + /** + * Sets the y component of the arrow tail about the arrow head. + * If `ayref` is `pixel`, a positive (negative) + * component corresponds to an arrow pointing + * from bottom to top (top to bottom). + * If `ayref` is an axis, this is an absolute value on that axis, + * like `y`, NOT a relative value. + */ + ay: number; + + /** + * Indicates in what terms the tail of the annotation (ax,ay) + * is specified. If `pixel`, `ax` is a relative offset in pixels + * from `x`. If set to an x axis id (e.g. *x* or *x2*), `ax` is + * specified in the same terms as that axis. This is useful + * for trendline annotations which should continue to indicate + * the correct trend when zoomed. + */ + axref: "pixel" | XAxisName; + + /** + * Indicates in what terms the tail of the annotation (ax,ay) + * is specified. If `pixel`, `ay` is a relative offset in pixels + * from `y`. If set to a y axis id (e.g. *y* or *y2*), `ay` is + * specified in the same terms as that axis. This is useful + * for trendline annotations which should continue to indicate + * the correct trend when zoomed. + */ + ayref: "pixel" | YAxisName; + + /** + * Sets the annotation's x coordinate axis. + * If set to an x axis id (e.g. *x* or *x2*), the `x` position refers to an x coordinate + * If set to *paper*, the `x` position refers to the distance from + * the left side of the plotting area in normalized coordinates + * where 0 (1) corresponds to the left (right) side. + */ + xref: "paper" | XAxisName; + + /** + * Sets the annotation's x position. + * If the axis `type` is *log*, then you must take the log of your desired range. + * If the axis `type` is *date*, it should be date strings, like date data, + * though Date objects and unix milliseconds will be accepted and converted to strings. + * If the axis `type` is *category*, it should be numbers, using the scale where each + * category is assigned a serial number from zero in the order it appears. + */ + x: number | string; + + /** + * Sets the text box's horizontal position anchor + * This anchor binds the `x` position to the *left*, *center* or *right* of the annotation. + * For example, if `x` is set to 1, `xref` to *paper* and `xanchor` to *right* then the + * right-most portion of the annotation lines up with the right-most edge of the plotting area. + * If *auto*, the anchor is equivalent to *center* for data-referenced annotations or if there + * is an arrow, whereas for paper-referenced with no arrow, the anchor picked corresponds to the closest side. + */ + xanchor: "auto" | "left" | "center" | "right"; + + /** + * Shifts the position of the whole annotation and arrow to the + * right (positive) or left (negative) by this many pixels. + */ + xshift: number; + + /** + * Sets the annotation's y coordinate axis. + * If set to an y axis id (e.g. *y* or *y2*), the `y` position refers to an y coordinate + * If set to *paper*, the `y` position refers to the distance from + * the bottom of the plotting area in normalized coordinates + * where 0 (1) corresponds to the bottom (top). + */ + yref: "paper" | YAxisName; + + /** + * Sets the annotation's y position. + * If the axis `type` is *log*, then you must take the log of your desired range. + * If the axis `type` is *date*, it should be date strings, like date data, + * though Date objects and unix milliseconds will be accepted and converted to strings. + * If the axis `type` is *category*, it should be numbers, using the scale where each + * category is assigned a serial number from zero in the order it appears. + */ + y: number | string; + + /** + * Sets the text box's vertical position anchor + * This anchor binds the `y` position to the *top*, *middle* or *bottom* of the annotation. + * For example, if `y` is set to 1, `yref` to *paper* and `yanchor` to *top* then the + * top-most portion of the annotation lines up with the top-most edge of the plotting area. + * If *auto*, the anchor is equivalent to *middle* for data-referenced annotations or if + * there is an arrow, whereas for paper-referenced with no arrow, the anchor picked + * corresponds to the closest side. + */ + yanchor: "auto" | "top" | "middle" | "bottom"; + + /** + * Shifts the position of the whole annotation and arrow up + * (positive) or down (negative) by this many pixels. + */ + yshift: number; + + /** + * Makes this annotation respond to clicks on the plot. + * If you click a data point that exactly matches the `x` and `y` values of this annotation, + * and it is hidden (visible: false), it will appear. In *onoff* mode, you must click the same + * point again to make it disappear, so if you click multiple points, you can show multiple + * annotations. In *onout* mode, a click anywhere else in the plot (on another data point or not) + * will hide this annotation. If you need to show/hide this annotation in response to different + * `x` or `y` values, you can set `xclick` and/or `yclick`. This is useful for example to label + * the side of a bar. To label markers though, `standoff` is preferred over `xclick` and `yclick`. + */ + clicktoshow: false | "onoff" | "onout"; + + /** + * Toggle this annotation when clicking a data point whose `x` value + * is `xclick` rather than the annotation's `x` value. + */ + xclick: any; + + /** + * Toggle this annotation when clicking a data point whose `y` value + * is `yclick` rather than the annotation's `y` value. + */ + yclick: any; + + /** + * Sets text to appear when hovering over this annotation. + * If omitted or blank, no hover label will appear. + */ + hovertext: string; + + hoverlabel: Partial; + + /** + * Determines whether the annotation text box captures mouse move and click events, + * or allows those events to pass through to data points in the plot that may be + * behind the annotation. By default `captureevents` is *false* unless `hovertext` + * is provided. If you use the event `plotly_clickannotation` without `hovertext` + * you must explicitly enable `captureevents`. + */ + captureevents: boolean; +} + +export interface Domain { + x: number[]; + y: number[]; + row: number; + column: number; +} + +export interface Padding { + /** + * The amount of padding (in px) along the top of the component. + */ + t: number; + /** + * The amount of padding (in px) on the right side of the component. + */ + r: number; + /** + * The amount of padding (in px) along the bottom of the component. + */ + b: number; + /** + * The amount of padding (in px) on the left side of the component. + */ + l: number; + editType: "arraydraw"; +} + +/** + * 'Sets the pattern within the marker. + */ +export interface Pattern { + /** + * Sets the shape of the pattern fill. + * By default, no pattern is used for filling the area. + */ + shape?: "" | "/" | "\\" | "x" | "-" | "|" | "+" | "."; + /** + * Determines whether `marker.color` should be used + * as a default to `bgcolor` or a `fgcolor`. + */ + fillmode?: "replace" | "overlay"; + /** + * When there is no colorscale sets the color of background pattern fill. + * Defaults to a `marker.color` background when `fillmode` is *overlay*. + * Otherwise, defaults to a transparent background. + */ + bgcolor?: string; + /** + * When there is no colorscale sets the color of foreground pattern fill. + * Defaults to a `marker.color` background when `fillmode` is *replace*. + * Otherwise, defaults to dark grey or white + * to increase contrast with the `bgcolor`. + */ + fgcolor?: string; + /** + * Sets the opacity of the foreground pattern fill. + * Defaults to a 0.5 when `fillmode` is *overlay*. + * Otherwise, defaults to 1. + */ + fgopacity?: string; + /** + * Sets the size of unit squares of the pattern fill in pixels, + * which corresponds to the interval of repetition of the pattern. + */ + size?: number; + /** + * Sets the solidity of the pattern fill. + * Solidity is roughly the fraction of the area filled by the pattern. + * Solidity of 0 shows only the background color without pattern + * and solidty of 1 shows only the foreground color without pattern. + */ + solidity?: number; +} diff --git a/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts b/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts index d570f55a927c4..834971cf7a1d5 100644 --- a/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts +++ b/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts @@ -1,7 +1,6 @@ /* eslint-disable one-var */ /* eslint-disable vars-on-top */ /* eslint-disable no-var */ -/* eslint-disable @typescript-eslint/no-explicit-any */ import * as React from 'react'; import { bin as d3Bin, extent as d3Extent, sum as d3Sum, min as d3Min, max as d3Max, merge as d3Merge } from 'd3-array'; import { scaleLinear as d3ScaleLinear } from 'd3-scale'; @@ -28,42 +27,82 @@ import { DataVizPalette, getNextColor } from '../../utilities/colors'; import { GaugeChartVariant, IGaugeChartProps, IGaugeChartSegment } from '../GaugeChart/index'; import { IGroupedVerticalBarChartProps } from '../GroupedVerticalBarChart/index'; import { IVerticalBarChartProps } from '../VerticalBarChart/index'; +import { Layout, PlotlySchema, PieData, PlotData, SankeyData } from './PlotlySchema'; +import type { Datum, TypedArray } from './PlotlySchema'; const isDate = (value: any): boolean => !isNaN(Date.parse(value)); const isNumber = (value: any): boolean => !isNaN(parseFloat(value)) && isFinite(value); -export const isDateArray = (array: any[]): boolean => isArrayOrTypedArray(array) && array.every(isDate); -export const isNumberArray = (array: any[]): boolean => isArrayOrTypedArray(array) && array.every(isNumber); -export const isMonthArray = (array: any[]): boolean => { - if (array && array.length > 0) { + +// const isDate = (datum: any): datum is Date => { +// return datum instanceof Date; +// }; + +const isMonth = (possiblyMonthValue: any, presentYear: number): boolean => { + return isDate(`${possiblyMonthValue} 01, ${presentYear}`); +}; + +export const isDateArray = (data: Datum[] | Datum[][] | TypedArray): boolean => { + if (!isArrayOrTypedArray(data)) { + return false; + } + + if (Array.isArray(data[0])) { + // Handle 2D array + return (data as Datum[][]).every(innerArray => innerArray.every(isDate)); + } else { + // Handle 1D array + return (data as Datum[]).every(isDate); + } +}; + +export const isNumberArray = (data: Datum[] | Datum[][] | TypedArray): boolean => { + if (!isArrayOrTypedArray(data)) { + return false; + } + + if (Array.isArray(data[0])) { + // Handle 2D array + return (data as Datum[][]).every(innerArray => innerArray.every(isNumber)); + } else { + // Handle 1D array + return (data as Datum[]).every(isNumber); + } +}; + +export const isMonthArray = (data: Datum[] | Datum[][] | TypedArray): boolean => { + if (!isArrayOrTypedArray(data)) { + return false; +} + if (data.length > 0) { const presentYear = new Date().getFullYear(); - return array.every(possiblyMonthValue => { - return isDate(`${possiblyMonthValue} 01, ${presentYear}`); - }); + if (Array.isArray(data[0])) { + // Handle 2D array + return (data as Datum[][]).every(innerArray => innerArray.every(possiblyMonthValue => isMonth(possiblyMonthValue, presentYear))); + } else { + // Handle 1D array + return (data as Datum[]).every(possiblyMonthValue => isMonth(possiblyMonthValue, presentYear)); + } } return false; }; -function getTitles(layout: any) { +function getTitles(layout: Partial | undefined) { const titles = { chartTitle: - typeof layout.title === 'string' ? layout.title : typeof layout.title?.text === 'string' ? layout.title.text : '', + typeof layout?.title === 'string' ? layout.title : layout?.title?.text?? '', xAxisTitle: typeof layout?.xaxis?.title === 'string' ? layout?.xaxis?.title - : typeof layout?.xaxis?.title?.text === 'string' - ? layout?.xaxis?.title?.text - : '', + : layout?.xaxis?.title?.text?? '', yAxisTitle: typeof layout?.yaxis?.title === 'string' ? layout?.yaxis?.title - : typeof layout?.yaxis?.title?.text === 'string' - ? layout?.yaxis?.title?.text - : '', + : layout?.yaxis?.title?.text?? '', }; return titles; } -export const updateXValues = (xValues: any[]): any[] => { +export const updateXValues = (xValues: Datum[] | Datum[][] | TypedArray): any[] => { const presentYear = new Date().getFullYear(); const dates = xValues.map(possiblyMonthValue => { const parsedDate = `${possiblyMonthValue} 01, ${presentYear}`; @@ -100,24 +139,23 @@ export const getColor = ( }; export const transformPlotlyJsonToDonutProps = ( - jsonObj: any, + input: PlotlySchema, colorMap: React.MutableRefObject>, isDarkTheme?: boolean, ): IDonutChartProps => { - const { data, layout } = jsonObj; - const firstData = data[0]; + const firstData = input.data[0] as PieData; const donutData = firstData.labels?.map((label: string, index: number): IChartDataPoint => { const color = getColor(label, colorMap, isDarkTheme); return { legend: label, - data: firstData.values?.[index], + data: firstData.values?.[index] as number, //ToDo how to handle string data? color, }; }); - const width: number = typeof layout?.width === 'number' ? layout?.width : 440; - const height: number = typeof layout?.height === 'number' ? layout?.height : 220; + const width: number = typeof input.layout?.width === 'number' ? input.layout?.width : 440; + const height: number = typeof input.layout?.height === 'number' ? input.layout?.height : 220; const hideLabels: boolean = firstData.textinfo ? !['value', 'percent', 'label+percent'].includes(firstData.textinfo) : false; @@ -135,14 +173,14 @@ export const transformPlotlyJsonToDonutProps = ( }, }; - const { chartTitle } = getTitles(layout); + const { chartTitle } = getTitles(input.layout); return { data: { chartTitle, chartData: donutData, }, - hideLegend: layout?.showlegend === false ? true : false, + hideLegend: input.layout?.showlegend === false ? true : false, width, height, innerRadius, @@ -153,15 +191,14 @@ export const transformPlotlyJsonToDonutProps = ( }; export const transformPlotlyJsonToVSBCProps = ( - jsonObj: any, + input: PlotlySchema, colorMap: React.MutableRefObject>, isDarkTheme?: boolean, ): IVerticalStackedBarChartProps => { - const { data, layout } = jsonObj; const mapXToDataPoints: { [key: string]: IVerticalStackedChartProps } = {}; let yMaxValue = 0; - data.forEach((series: any, index1: number) => { + input.data.forEach((series: PlotData, index1: number) => { series.x?.forEach((x: string | number, index2: number) => { if (!mapXToDataPoints[x]) { mapXToDataPoints[x] = { xAxisPoint: x, chartData: [], lineData: [] }; @@ -187,7 +224,7 @@ export const transformPlotlyJsonToVSBCProps = ( }); }); - const { chartTitle, xAxisTitle, yAxisTitle } = getTitles(layout); + const { chartTitle, xAxisTitle, yAxisTitle } = getTitles(input.layout); return { data: Object.values(mapXToDataPoints), @@ -203,14 +240,13 @@ export const transformPlotlyJsonToVSBCProps = ( }; export const transformPlotlyJsonToGVBCProps = ( - jsonObj: any, + input: PlotlySchema, colorMap: React.MutableRefObject>, isDarkTheme?: boolean, ): IGroupedVerticalBarChartProps => { - const { data, layout } = jsonObj; const mapXToDataPoints: Record = {}; - data.forEach((series: any, index1: number) => { + input.data.forEach((series: PlotData, index1: number) => { series.x?.forEach((x: string | number, index2: number) => { if (!mapXToDataPoints[x]) { mapXToDataPoints[x] = { name: x.toString(), series: [] }; @@ -230,7 +266,7 @@ export const transformPlotlyJsonToGVBCProps = ( }); }); - const { chartTitle, xAxisTitle, yAxisTitle } = getTitles(layout); + const { chartTitle, xAxisTitle, yAxisTitle } = getTitles(input.layout); return { data: Object.values(mapXToDataPoints), @@ -245,14 +281,13 @@ export const transformPlotlyJsonToGVBCProps = ( }; export const transformPlotlyJsonToVBCProps = ( - jsonObj: any, + input: PlotlySchema, colorMap: React.MutableRefObject>, isDarkTheme?: boolean, ): IVerticalBarChartProps => { - const { data, layout } = jsonObj; const vbcData: IVerticalBarChartDataPoint[] = []; - data.forEach((series: any, index: number) => { + input.data.forEach((series: PlotData, index: number) => { if (!series.x) { return; } @@ -320,7 +355,7 @@ export const transformPlotlyJsonToVBCProps = ( }); }); - const { chartTitle, xAxisTitle, yAxisTitle } = getTitles(layout); + const { chartTitle, xAxisTitle, yAxisTitle } = getTitles(input.layout); return { data: vbcData, @@ -336,14 +371,13 @@ export const transformPlotlyJsonToVBCProps = ( }; export const transformPlotlyJsonToScatterChartProps = ( - jsonObj: any, + input: PlotlySchema, isAreaChart: boolean, colorMap: React.MutableRefObject>, isDarkTheme?: boolean, ): ILineChartProps | IAreaChartProps => { - const { data, layout } = jsonObj; - const chartData: ILineChartPoints[] = data.map((series: any, index: number) => { + const chartData: ILineChartPoints[] = input.data.map((series: PlotData, index: number) => { const xValues = series.x; const isString = typeof xValues[0] === 'string'; const isXDate = isDateArray(xValues); @@ -361,7 +395,7 @@ export const transformPlotlyJsonToScatterChartProps = ( }; }); - const { chartTitle, xAxisTitle, yAxisTitle } = getTitles(layout); + const { chartTitle, xAxisTitle, yAxisTitle } = getTitles(input.layout); const chartProps: IChartProps = { chartTitle, @@ -386,14 +420,13 @@ export const transformPlotlyJsonToScatterChartProps = ( }; export const transformPlotlyJsonToHorizontalBarWithAxisProps = ( - jsonObj: any, + input: PlotlySchema, colorMap: React.MutableRefObject>, isDarkTheme?: boolean, ): IHorizontalBarChartWithAxisProps => { - const { data, layout } = jsonObj; - const chartData: IHorizontalBarChartWithAxisDataPoint[] = data - .map((series: any, index: number) => { + const chartData: IHorizontalBarChartWithAxisDataPoint[] = input.data + .map((series: PlotData, index: number) => { return series.y.map((yValue: string, i: number) => { const color = getColor(yValue, colorMap, isDarkTheme); return { @@ -406,16 +439,16 @@ export const transformPlotlyJsonToHorizontalBarWithAxisProps = ( }) .flat(); - const chartHeight: number = typeof layout.height === 'number' ? layout.height : 450; - const margin: number = typeof layout.margin?.l === 'number' ? layout.margin?.l : 0; - const padding: number = typeof layout.margin?.pad === 'number' ? layout.margin?.pad : 0; + const chartHeight: number = typeof input.layout?.height === 'number' ? input.layout.height : 450; + const margin: number = typeof input.layout?.margin?.l === 'number' ? input.layout.margin?.l : 0; + const padding: number = typeof input.layout?.margin?.pad === 'number' ? input.layout.margin?.pad : 0; const availableHeight: number = chartHeight - margin - padding; - const numberOfBars = data[0].y.length; + const numberOfBars = (input.data[0] as PlotData).y.length; const scalingFactor = 0.01; const gapFactor = 1 / (1 + scalingFactor * numberOfBars); const barHeight = availableHeight / (numberOfBars * (1 + gapFactor)); - const { chartTitle, xAxisTitle, yAxisTitle } = getTitles(layout); + const { chartTitle, xAxisTitle, yAxisTitle } = getTitles(input.layout); return { data: chartData, @@ -423,32 +456,31 @@ export const transformPlotlyJsonToHorizontalBarWithAxisProps = ( xAxisTitle, yAxisTitle, secondaryYAxistitle: - typeof layout?.yaxis2?.title === 'string' ? layout?.yaxis2?.title : layout?.yaxis2?.title?.text || '', + typeof input.layout?.yaxis2?.title === 'string' ? input.layout?.yaxis2?.title : input.layout?.yaxis2?.title?.text || '', barHeight, showYAxisLables: true, styles: { root: { height: chartHeight, - width: typeof layout.width === 'number' ? layout.width : 600, + width: typeof input.layout?.width === 'number' ? input.layout.width : 600, }, }, }; }; -export const transformPlotlyJsonToHeatmapProps = (jsonObj: any): IHeatMapChartProps => { - const { data, layout } = jsonObj; - const firstData = data[0]; +export const transformPlotlyJsonToHeatmapProps = (input: PlotlySchema): IHeatMapChartProps => { + const firstData = input.data[0] as PlotData; const heatmapDataPoints: IHeatMapChartDataPoint[] = []; let zMin = Number.POSITIVE_INFINITY; let zMax = Number.NEGATIVE_INFINITY; - firstData.x?.forEach((xVal: any, xIdx: number) => { + firstData.x?.forEach((xVal: PlotData, xIdx: number) => { firstData.y?.forEach((yVal: any, yIdx: number) => { const zVal = firstData.z?.[yIdx]?.[xIdx]; heatmapDataPoints.push({ - x: layout.xaxis?.type === 'date' ? new Date(xVal) : xVal, - y: layout.yaxis?.type === 'date' ? new Date(yVal) : yVal, + x: input.layout?.xaxis?.type === 'date' ? new Date(xVal) : xVal, + y: input.layout?.yaxis?.type === 'date' ? new Date(yVal) : yVal, value: zVal, rectText: zVal, }); @@ -468,7 +500,7 @@ export const transformPlotlyJsonToHeatmapProps = (jsonObj: any): IHeatMapChartPr ? firstData.colorscale.map((arr: any) => arr[0] * (zMax - zMin) + zMin) : []; const rangeValuesForColorScale: string[] = firstData.colorscale ? firstData.colorscale.map((arr: any) => arr[1]) : []; - const { chartTitle, xAxisTitle, yAxisTitle } = getTitles(layout); + const { chartTitle, xAxisTitle, yAxisTitle } = getTitles(input.layout); return { data: [heatmapData], @@ -484,25 +516,23 @@ export const transformPlotlyJsonToHeatmapProps = (jsonObj: any): IHeatMapChartPr }; export const transformPlotlyJsonToSankeyProps = ( - jsonObj: any, + input: PlotlySchema, colorMap: React.MutableRefObject>, isDarkTheme?: boolean, ): ISankeyChartProps => { - const { data, layout } = jsonObj; - const { link, node } = data[0]; - const validLinks = link.value + const { link, node } = input.data[0] as SankeyData; + const validLinks = link?.value //ToDo handle undefined links .map((val: number, index: number) => ({ value: val, - source: link.source[index], - target: link.target[index], + source: link?.source[index], + target: link?.target[index], })) // eslint-disable-next-line @typescript-eslint/no-shadow - //@ts-expect-error Dynamic link object. Ignore for now. // Filter out negative nodes, unequal nodes and self-references (circular links) .filter(x => x.source >= 0 && x.target >= 0 && x.source !== x.target); const sankeyChartData = { - nodes: node.label.map((label: string, index: number) => { + nodes: node.label?.map((label: string, index: number) => { const color = getColor(label, colorMap, isDarkTheme); return { @@ -518,16 +548,16 @@ export const transformPlotlyJsonToSankeyProps = ( }), }; - const width: number = typeof layout?.width === 'number' ? layout?.width : 440; - const height: number = typeof layout?.height === 'number' ? layout?.height : 220; + const width: number = typeof input.layout?.width === 'number' ? input.layout?.width : 440; + const height: number = typeof input.layout?.height === 'number' ? input.layout?.height : 220; const styles: ISankeyChartProps['styles'] = { root: { - ...(typeof layout.font?.size === 'number' ? { fontSize: layout.font?.size } : {}), + ...(typeof input.layout?.font?.size === 'number' ? { fontSize: input.layout.font?.size } : {}), }, }; const shouldResize: number = width + height; - const { chartTitle } = getTitles(layout); + const { chartTitle } = getTitles(input.layout); return { data: { @@ -543,12 +573,11 @@ export const transformPlotlyJsonToSankeyProps = ( }; export const transformPlotlyJsonToGaugeProps = ( - jsonObj: any, + input: PlotlySchema, colorMap: React.MutableRefObject>, isDarkTheme?: boolean, ): IGaugeChartProps => { - const { data, layout } = jsonObj; - const firstData = data[0]; + const firstData = input.data[0] as PlotData; const segments = firstData.gauge?.steps?.length ? firstData.gauge.steps.map((step: any, index: number): IGaugeChartSegment => { @@ -579,11 +608,11 @@ export const transformPlotlyJsonToGaugeProps = ( const diff = firstData.value - firstData.delta.reference; if (diff >= 0) { sublabel = `\u25B2 ${diff}`; - const color = getColor(firstData.delta.increasing?.color || '', colorMap, isDarkTheme); + const color = getColor('inpr', colorMap, isDarkTheme); sublabelColor = color; } else { sublabel = `\u25BC ${Math.abs(diff)}`; - const color = getColor(firstData.delta.decreasing?.color || '', colorMap, isDarkTheme); + const color = getColor('inpr', colorMap, isDarkTheme); sublabelColor = color; } } @@ -594,7 +623,7 @@ export const transformPlotlyJsonToGaugeProps = ( }, }; - const { chartTitle } = getTitles(layout); + const { chartTitle } = getTitles(input.layout); return { segments, @@ -605,8 +634,8 @@ export const transformPlotlyJsonToGaugeProps = ( minValue: typeof firstData.gauge?.axis?.range?.[0] === 'number' ? firstData.gauge?.axis?.range?.[0] : undefined, maxValue: typeof firstData.gauge?.axis?.range?.[1] === 'number' ? firstData.gauge?.axis?.range?.[1] : undefined, chartValueFormat: () => firstData.value, - width: typeof layout?.width === 'number' ? layout?.width : 440, - height: typeof layout?.height === 'number' ? layout?.height : 220, + width: typeof input.layout?.width === 'number' ? input.layout?.width : 440, + height: typeof input.layout?.height === 'number' ? input.layout?.height : 220, styles, variant: firstData.gauge?.steps?.length ? GaugeChartVariant.MultipleSegments : GaugeChartVariant.SingleSegment, }; From d36c6be9e7a30b3fb84e9cbc24fafda556b3664f Mon Sep 17 00:00:00 2001 From: "Atishay Jain (atisjai)" Date: Sun, 12 Jan 2025 21:49:41 +0530 Subject: [PATCH 02/20] Remove explicit type checking --- .../DeclarativeChart/PlotlySchemaAdapter.ts | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts b/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts index 834971cf7a1d5..f7acee4ad5fa4 100644 --- a/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts +++ b/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts @@ -154,8 +154,8 @@ export const transformPlotlyJsonToDonutProps = ( }; }); - const width: number = typeof input.layout?.width === 'number' ? input.layout?.width : 440; - const height: number = typeof input.layout?.height === 'number' ? input.layout?.height : 220; + const width: number = input.layout?.width?? 440; + const height: number = input.layout?.height?? 220; const hideLabels: boolean = firstData.textinfo ? !['value', 'percent', 'label+percent'].includes(firstData.textinfo) : false; @@ -439,9 +439,9 @@ export const transformPlotlyJsonToHorizontalBarWithAxisProps = ( }) .flat(); - const chartHeight: number = typeof input.layout?.height === 'number' ? input.layout.height : 450; - const margin: number = typeof input.layout?.margin?.l === 'number' ? input.layout.margin?.l : 0; - const padding: number = typeof input.layout?.margin?.pad === 'number' ? input.layout.margin?.pad : 0; + const chartHeight: number = input.layout?.height?? 450; + const margin: number = input.layout?.margin?.l?? 0; + const padding: number = input.layout?.margin?.pad?? 0; const availableHeight: number = chartHeight - margin - padding; const numberOfBars = (input.data[0] as PlotData).y.length; const scalingFactor = 0.01; @@ -490,7 +490,7 @@ export const transformPlotlyJsonToHeatmapProps = (input: PlotlySchema): IHeatMap }); }); const heatmapData: IHeatMapChartData = { - legend: typeof firstData.name === 'string' ? firstData.name : '', + legend: firstData.name, data: heatmapDataPoints, value: 0, }; @@ -548,8 +548,8 @@ export const transformPlotlyJsonToSankeyProps = ( }), }; - const width: number = typeof input.layout?.width === 'number' ? input.layout?.width : 440; - const height: number = typeof input.layout?.height === 'number' ? input.layout?.height : 220; + const width: number = input.layout?.width?? 440; + const height: number = input.layout?.height?? 220; const styles: ISankeyChartProps['styles'] = { root: { ...(typeof input.layout?.font?.size === 'number' ? { fontSize: input.layout.font?.size } : {}), @@ -627,15 +627,15 @@ export const transformPlotlyJsonToGaugeProps = ( return { segments, - chartValue: typeof firstData.value === 'number' ? firstData.value : 0, + chartValue: firstData.value?? 0, chartTitle, sublabel, // range values can be null minValue: typeof firstData.gauge?.axis?.range?.[0] === 'number' ? firstData.gauge?.axis?.range?.[0] : undefined, maxValue: typeof firstData.gauge?.axis?.range?.[1] === 'number' ? firstData.gauge?.axis?.range?.[1] : undefined, chartValueFormat: () => firstData.value, - width: typeof input.layout?.width === 'number' ? input.layout?.width : 440, - height: typeof input.layout?.height === 'number' ? input.layout?.height : 220, + width: input.layout?.width?? 440, + height: input.layout?.height?? 220, styles, variant: firstData.gauge?.steps?.length ? GaugeChartVariant.MultipleSegments : GaugeChartVariant.SingleSegment, }; From 437be6d9db8fb9176af879499400dc8f1d2c9c5a Mon Sep 17 00:00:00 2001 From: "Atishay Jain (atisjai)" Date: Mon, 13 Jan 2025 08:54:21 +0530 Subject: [PATCH 03/20] Fix compilation errors --- .../DeclarativeChart/PlotlySchemaAdapter.ts | 105 ++++++++++++------ 1 file changed, 73 insertions(+), 32 deletions(-) diff --git a/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts b/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts index f7acee4ad5fa4..8574f88dfbf5c 100644 --- a/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts +++ b/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts @@ -16,6 +16,7 @@ import { IHeatMapChartDataPoint, IGroupedVerticalBarChartData, IVerticalBarChartDataPoint, + ISankeyChartData, } from '../../types/IDataPoint'; import { ISankeyChartProps } from '../SankeyChart/index'; import { IVerticalStackedBarChartProps } from '../VerticalStackedBarChart/index'; @@ -23,7 +24,7 @@ import { IHorizontalBarChartWithAxisProps } from '../HorizontalBarChartWithAxis/ import { ILineChartProps } from '../LineChart/index'; import { IAreaChartProps } from '../AreaChart/index'; import { IHeatMapChartProps } from '../HeatMapChart/index'; -import { DataVizPalette, getNextColor } from '../../utilities/colors'; +import { /* DataVizPalette, */getNextColor } from '../../utilities/colors'; import { GaugeChartVariant, IGaugeChartProps, IGaugeChartSegment } from '../GaugeChart/index'; import { IGroupedVerticalBarChartProps } from '../GroupedVerticalBarChart/index'; import { IVerticalBarChartProps } from '../VerticalBarChart/index'; @@ -104,7 +105,10 @@ function getTitles(layout: Partial | undefined) { export const updateXValues = (xValues: Datum[] | Datum[][] | TypedArray): any[] => { const presentYear = new Date().getFullYear(); - const dates = xValues.map(possiblyMonthValue => { + if (xValues.length > 0 && Array.isArray(xValues[0])) { + throw new Error('updateXValues:: 2D array not supported'); + } + const dates = (xValues as Datum[]).map(possiblyMonthValue => { const parsedDate = `${possiblyMonthValue} 01, ${presentYear}`; return isDate(parsedDate) ? new Date(parsedDate) : null; }); @@ -119,7 +123,7 @@ export const updateXValues = (xValues: Datum[] | Datum[][] | TypedArray): any[] dates[i - 1]!.setFullYear(currentYear); } } - xValues = xValues.map((month, index) => { + xValues = (xValues as Datum[]).map((month, index) => { return `${month} 01, ${dates[index]!.getFullYear()}`; }); return xValues; @@ -199,28 +203,39 @@ export const transformPlotlyJsonToVSBCProps = ( let yMaxValue = 0; input.data.forEach((series: PlotData, index1: number) => { - series.x?.forEach((x: string | number, index2: number) => { + if (series.x?.length > 0 && Array.isArray(series.x[0])) { + throw new Error('transform to VSBC:: 2D x array not supported'); + } + if (series.y?.length > 0 && Array.isArray(series.y[0])) { + throw new Error('transform to VSBC:: 2D y array not supported'); + } + if (!isNumberArray(series.y)) { + throw new Error('transform to VSBC:: y values are not numbers'); + } + + (series.x as Datum[])?.forEach((x: string | number, index2: number) => { if (!mapXToDataPoints[x]) { mapXToDataPoints[x] = { xAxisPoint: x, chartData: [], lineData: [] }; } const legend: string = series.name || `Series ${index1 + 1}`; + const yVal: number = series.y?.[index2] as number?? 0 if (series.type === 'bar' || series.type === 'scatter') { const color = getColor(legend, colorMap, isDarkTheme); mapXToDataPoints[x].chartData.push({ legend, - data: series.y?.[index2], + data: yVal, color, }); - } else if (series.type === 'line') { + } /* else if (series.type === 'line') { //ToDo - Fix this as series.type cannot be line type const color = getColor(legend, colorMap, isDarkTheme); mapXToDataPoints[x].lineData!.push({ legend, - y: series.y?.[index2], + y: yVal, color, }); - } + } */ - yMaxValue = Math.max(yMaxValue, series.y?.[index2]); + yMaxValue = Math.max(yMaxValue, yVal); }); }); @@ -247,7 +262,16 @@ export const transformPlotlyJsonToGVBCProps = ( const mapXToDataPoints: Record = {}; input.data.forEach((series: PlotData, index1: number) => { - series.x?.forEach((x: string | number, index2: number) => { + if (series.x?.length > 0 && Array.isArray(series.x[0])) { + throw new Error('transform to GVBC:: 2D x array not supported'); + } + if (series.y?.length > 0 && Array.isArray(series.y[0])) { + throw new Error('transform to GVBC:: 2D y array not supported'); + } + if (!isNumberArray(series.y)) { + throw new Error('transform to GVBC:: y values are not numbers'); + } + (series.x as Datum[])?.forEach((x: string | number, index2: number) => { if (!mapXToDataPoints[x]) { mapXToDataPoints[x] = { name: x.toString(), series: [] }; } @@ -257,7 +281,7 @@ export const transformPlotlyJsonToGVBCProps = ( mapXToDataPoints[x].series.push({ key: legend, - data: series.y?.[index2], + data: (series.y?.[index2] as number)?? 0, xAxisCalloutData: x as string, color, legend, @@ -288,12 +312,16 @@ export const transformPlotlyJsonToVBCProps = ( const vbcData: IVerticalBarChartDataPoint[] = []; input.data.forEach((series: PlotData, index: number) => { + if (series.x?.length > 0 && Array.isArray(series.x[0])) { + throw new Error('transform to GVBC:: 2D x array not supported'); + } + if (!series.x) { return; } const scale = d3ScaleLinear() - .domain(d3Extent(series.x) as [number, number]) + .domain(d3Extent(series.x as number[]) as [number, number]) .nice(); let [xMin, xMax] = scale.domain(); @@ -318,7 +346,7 @@ export const transformPlotlyJsonToVBCProps = ( bin.domain([xMin, xMax]).thresholds(thresholds); } - const buckets = bin(series.x); + const buckets = bin(series.x as number[]); // If the start or end of xbins is specified, then the number of datapoints may become less than x.length const totalDataPoints = d3Merge(buckets).length; @@ -378,7 +406,14 @@ export const transformPlotlyJsonToScatterChartProps = ( ): ILineChartProps | IAreaChartProps => { const chartData: ILineChartPoints[] = input.data.map((series: PlotData, index: number) => { - const xValues = series.x; + if (series.x?.length > 0 && Array.isArray(series.x[0])) { + throw new Error('transform to Scatter:: 2D x array not supported'); + } + if (series.y?.length > 0 && Array.isArray(series.y[0])) { + throw new Error('transform to Scatter:: 2D y array not supported'); + } + + const xValues = series.x as Datum[]; const isString = typeof xValues[0] === 'string'; const isXDate = isDateArray(xValues); const isXNumber = isNumberArray(xValues); @@ -387,12 +422,12 @@ export const transformPlotlyJsonToScatterChartProps = ( return { legend, - data: xValues.map((x: string | number, i: number) => ({ - x: isString ? (isXDate ? new Date(x) : isXNumber ? parseFloat(x as string) : x) : x, + data: xValues.map((x, i: number) => ({ + x: isString ? (isXDate ? new Date(x as string) : isXNumber ? parseFloat(x as string) : x) : x, y: series.y[i], })), color: lineColor, - }; + } as ILineChartPoints; }); const { chartTitle, xAxisTitle, yAxisTitle } = getTitles(input.layout); @@ -427,14 +462,20 @@ export const transformPlotlyJsonToHorizontalBarWithAxisProps = ( const chartData: IHorizontalBarChartWithAxisDataPoint[] = input.data .map((series: PlotData, index: number) => { - return series.y.map((yValue: string, i: number) => { + if (series.x?.length > 0 && Array.isArray(series.x[0])) { + throw new Error('transform to HBC:: 2D x array not supported'); + } + if (series.y?.length > 0 && Array.isArray(series.y[0])) { + throw new Error('transform to HBC:: 2D y array not supported'); + } + return (series.y as Datum[]).map((yValue: string, i: number) => { const color = getColor(yValue, colorMap, isDarkTheme); return { x: series.x[i], y: yValue, legend: yValue, color, - }; + } as IHorizontalBarChartWithAxisDataPoint; }); }) .flat(); @@ -474,13 +515,13 @@ export const transformPlotlyJsonToHeatmapProps = (input: PlotlySchema): IHeatMap let zMin = Number.POSITIVE_INFINITY; let zMax = Number.NEGATIVE_INFINITY; - firstData.x?.forEach((xVal: PlotData, xIdx: number) => { + (firstData.x as Datum[])?.forEach((xVal, xIdx: number) => { firstData.y?.forEach((yVal: any, yIdx: number) => { - const zVal = firstData.z?.[yIdx]?.[xIdx]; + const zVal = (firstData.z as number[][])?.[yIdx]?.[xIdx]; heatmapDataPoints.push({ - x: input.layout?.xaxis?.type === 'date' ? new Date(xVal) : xVal, - y: input.layout?.yaxis?.type === 'date' ? new Date(yVal) : yVal, + x: input.layout?.xaxis?.type === 'date' ? xVal as Date : xVal?? 0, + y: input.layout?.yaxis?.type === 'date' ? yVal as Date : yVal, value: zVal, rectText: zVal, }); @@ -497,9 +538,9 @@ export const transformPlotlyJsonToHeatmapProps = (input: PlotlySchema): IHeatMap // Convert normalized values to actual values const domainValuesForColorScale: number[] = firstData.colorscale - ? firstData.colorscale.map((arr: any) => arr[0] * (zMax - zMin) + zMin) + ? (firstData.colorscale as Array<[number, string]>).map((arr) => arr[0] * (zMax - zMin) + zMin) : []; - const rangeValuesForColorScale: string[] = firstData.colorscale ? firstData.colorscale.map((arr: any) => arr[1]) : []; + const rangeValuesForColorScale: string[] = firstData.colorscale ? (firstData.colorscale as Array<[number, string]>).map((arr) => arr[1]) : []; const { chartTitle, xAxisTitle, yAxisTitle } = getTitles(input.layout); return { @@ -521,11 +562,11 @@ export const transformPlotlyJsonToSankeyProps = ( isDarkTheme?: boolean, ): ISankeyChartProps => { const { link, node } = input.data[0] as SankeyData; - const validLinks = link?.value //ToDo handle undefined links + const validLinks = link?.value?? [] .map((val: number, index: number) => ({ value: val, - source: link?.source[index], - target: link?.target[index], + source: link?.source![index], + target: link?.target![index], })) // eslint-disable-next-line @typescript-eslint/no-shadow // Filter out negative nodes, unequal nodes and self-references (circular links) @@ -546,7 +587,7 @@ export const transformPlotlyJsonToSankeyProps = ( ...validLink, }; }), - }; + } as ISankeyChartData; const width: number = input.layout?.width?? 440; const height: number = input.layout?.height?? 220; @@ -590,7 +631,7 @@ export const transformPlotlyJsonToGaugeProps = ( }; }) : [ - { +/* { //ToDo: fix this legend: 'Current', size: firstData.value ?? 0 - (firstData.gauge?.range?.[0] ?? 0), color: getColor('Current', colorMap, isDarkTheme), @@ -599,7 +640,7 @@ export const transformPlotlyJsonToGaugeProps = ( legend: 'Target', size: (firstData.gauge?.range?.[1] ?? 100) - (firstData.value ?? 0), color: DataVizPalette.disabled, - }, + }, */ ]; let sublabel: string | undefined; @@ -633,7 +674,7 @@ export const transformPlotlyJsonToGaugeProps = ( // range values can be null minValue: typeof firstData.gauge?.axis?.range?.[0] === 'number' ? firstData.gauge?.axis?.range?.[0] : undefined, maxValue: typeof firstData.gauge?.axis?.range?.[1] === 'number' ? firstData.gauge?.axis?.range?.[1] : undefined, - chartValueFormat: () => firstData.value, + //chartValueFormat: () => firstData.value, ToDo: fix this width: input.layout?.width?? 440, height: input.layout?.height?? 220, styles, From ab36c15ffd3526afaa2725c5671ca971edb294c8 Mon Sep 17 00:00:00 2001 From: "Atishay Jain (atisjai)" <98592573+AtishayMsft@users.noreply.github.com> Date: Mon, 13 Jan 2025 09:54:06 +0000 Subject: [PATCH 04/20] Fix lint errors --- .../DeclarativeChart/DeclarativeChart.tsx | 18 +- .../DeclarativeChart/PlotlySchema.ts | 3379 ++++++++--------- .../DeclarativeChart/PlotlySchemaAdapter.ts | 110 +- 3 files changed, 1751 insertions(+), 1756 deletions(-) diff --git a/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx b/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx index 26b022a1837de..829e8970c639d 100644 --- a/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx +++ b/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx @@ -23,9 +23,9 @@ import { transformPlotlyJsonToGVBCProps, transformPlotlyJsonToVBCProps, } from './PlotlySchemaAdapter'; -import { LineChart } from '../LineChart/index'; +import { LineChart, ILineChartProps } from '../LineChart/index'; import { HorizontalBarChartWithAxis } from '../HorizontalBarChartWithAxis/index'; -import { AreaChart } from '../AreaChart/index'; +import { AreaChart, IAreaChartProps } from '../AreaChart/index'; import { HeatMapChart } from '../HeatMapChart/index'; import { SankeyChart } from '../SankeyChart/SankeyChart'; import { GaugeChart } from '../GaugeChart/index'; @@ -42,6 +42,7 @@ export interface Schema { /** * Plotly schema represented as JSON object */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any plotlySchema: any; } @@ -192,8 +193,10 @@ export const DeclarativeChart: React.FunctionComponent = const isXDate = isDateArray(xValues); const isXNumber = isNumberArray(xValues); const isXMonth = isMonthArray(xValues); - const isAreaChart = plotlyInput.data.some((series: PlotData) => series.fill === 'tonexty' || series.fill === 'tozeroy'); - const renderChart = (chartProps: any) => { + const isAreaChart = plotlyInput.data.some( + (series: PlotData) => series.fill === 'tonexty' || series.fill === 'tozeroy', + ); + const renderChart = (chartProps: ILineChartProps | IAreaChartProps) => { if (isAreaChart) { return ( = x: updateXValues(dataPoint.x), })); const chartProps = { - ...transformPlotlyJsonToScatterChartProps({ data: updatedData, layout: plotlyInput.layout }, isAreaChart, colorMap, isDarkTheme), + ...transformPlotlyJsonToScatterChartProps( + { data: updatedData, layout: plotlyInput.layout }, + isAreaChart, + colorMap, + isDarkTheme, + ), legendProps, componentRef: chartRef, calloutProps: { layerProps: { eventBubblingEnabled: true } }, diff --git a/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchema.ts b/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchema.ts index e52a3998a22b4..e1ac07356818d 100644 --- a/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchema.ts +++ b/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchema.ts @@ -1,3 +1,6 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +/* eslint-disable @typescript-eslint/no-explicit-any */ + /** * This interface is extracted from Plotly.js typescript definitions. * All the unsupported types are removed to align with fluent charts. @@ -8,783 +11,783 @@ export type PieColor = string | number; export type PieColors = Array; export interface PieFont { - family: string | string[]; - size: number | number[]; - color: PieColor | PieColors; + family: string | string[]; + size: number | number[]; + color: PieColor | PieColors; } -export interface PieDataTitle extends Pick { - font: Partial; +export interface PieDataTitle extends Pick { + font: Partial; } -export type PieTextPosition = "inside" | "outside" | "auto" | "none"; +export type PieTextPosition = 'inside' | 'outside' | 'auto' | 'none'; export type PieHoverInfo = - | "all" - | "none" - | "skip" - | "label" - | "text" - | "value" - | "percent" - | "name" - | "label+text" - | "label+value" - | "label+percent" - | "label+name" - | "text+value" - | "text+percent" - | "text+name" - | "value+percent" - | "value+name" - | "percent+name" - | "label+text+value" - | "label+text+percent" - | "label+text+name" - | "label+value+percent" - | "label+value+name" - | "label+percent+name" - | "text+value+percent" - | "text+value+name" - | "text+percent+name" - | "value+percent+name" - | "label+text+value+percent" - | "label+text+value+name" - | "label+text+percent+name" - | "label+value+percent+name" - | "text+value+percent+name"; + | 'all' + | 'none' + | 'skip' + | 'label' + | 'text' + | 'value' + | 'percent' + | 'name' + | 'label+text' + | 'label+value' + | 'label+percent' + | 'label+name' + | 'text+value' + | 'text+percent' + | 'text+name' + | 'value+percent' + | 'value+name' + | 'percent+name' + | 'label+text+value' + | 'label+text+percent' + | 'label+text+name' + | 'label+value+percent' + | 'label+value+name' + | 'label+percent+name' + | 'text+value+percent' + | 'text+value+name' + | 'text+percent+name' + | 'value+percent+name' + | 'label+text+value+percent' + | 'label+text+value+name' + | 'label+text+percent+name' + | 'label+value+percent+name' + | 'text+value+percent+name'; export interface PieDomain { - x: number[]; - y: number[]; - row: number; - column: number; + x: number[]; + y: number[]; + row: number; + column: number; } export interface PieLine { - color: PieColor | PieColors; - width: number | number[]; + color: PieColor | PieColors; + width: number | number[]; } export interface PieMarker { - colors: PieColors; - line: Partial; + colors: PieColors; + line: Partial; } export interface PieHoverLabel { - bgcolor: PieColor | PieColors; - bordercolor: PieColor | PieColors; - font: PieFont; - align: HoverLabel["align"] | Array; - namelength: number | number[]; -} - -export type PieInsideTextOrientation = "horizontal" | "radial" | "tangential" | "auto"; - -export interface PieData extends - Pick< - PlotData, - | "name" - | "visible" - | "showlegend" - | "legendgroup" - | "opacity" - | "ids" - | "labels" - | "hovertext" - | "automargin" - | "textinfo" - | "direction" - | "hole" - | "rotation" - > -{ - type: "pie"; - title: Partial; - values: Array; - dlabel: number; - label0: number; - pull: number | number[]; - text: Datum | Datum[]; - textposition: PieTextPosition | PieTextPosition[]; - texttemplate: string | string[]; - hoverinfo: PieHoverInfo; - hovertemplate: string | string[]; - meta: number | string; - customdata: Datum[]; - domain: Partial; - marker: Partial; - textfont: PieFont; - hoverlabel: Partial; - insidetextfont: PieFont; - insidetextorientation: PieInsideTextOrientation; - outsidetextfont: PieFont; - scalegroup: string; - sort: boolean; - uirevision: number | string; + bgcolor: PieColor | PieColors; + bordercolor: PieColor | PieColors; + font: PieFont; + align: HoverLabel['align'] | Array; + namelength: number | number[]; +} + +export type PieInsideTextOrientation = 'horizontal' | 'radial' | 'tangential' | 'auto'; + +export interface PieData + extends Pick< + PlotData, + | 'name' + | 'visible' + | 'showlegend' + | 'legendgroup' + | 'opacity' + | 'ids' + | 'labels' + | 'hovertext' + | 'automargin' + | 'textinfo' + | 'direction' + | 'hole' + | 'rotation' + > { + type: 'pie'; + title: Partial; + values: Array; + dlabel: number; + label0: number; + pull: number | number[]; + text: Datum | Datum[]; + textposition: PieTextPosition | PieTextPosition[]; + texttemplate: string | string[]; + hoverinfo: PieHoverInfo; + hovertemplate: string | string[]; + meta: number | string; + customdata: Datum[]; + domain: Partial; + marker: Partial; + textfont: PieFont; + hoverlabel: Partial; + insidetextfont: PieFont; + insidetextorientation: PieInsideTextOrientation; + outsidetextfont: PieFont; + scalegroup: string; + sort: boolean; + uirevision: number | string; } export type SankeyColor = string | number; export type SankeyColors = Array; export interface SankeyFont { - family: string | string[]; - size: number | number[]; - color: SankeyColor | SankeyColors; + family: string | string[]; + size: number | number[]; + color: SankeyColor | SankeyColors; } export interface SankeyDataTitle { - font: Partial; - title: string; + font: Partial; + title: string; } -export type SankeyOrientation = "v" | "h"; +export type SankeyOrientation = 'v' | 'h'; export interface SankeyHoverLabel { - bgcolor: SankeyColor | SankeyColors; - bordercolor: SankeyColor | SankeyColors; - font: SankeyFont; - align: HoverLabel["align"] | Array; - namelength: number | number[]; + bgcolor: SankeyColor | SankeyColors; + bordercolor: SankeyColor | SankeyColors; + font: SankeyFont; + align: HoverLabel['align'] | Array; + namelength: number | number[]; } export interface SankeyDomain { - row: number; - column: number; - x: number[]; - y: number[]; + row: number; + column: number; + x: number[]; + y: number[]; } export interface SankeyNode { - color: SankeyColor[]; - customdata: Datum[]; - groups: SankeyNode[]; - hoverinfo: "all" | "none" | "skip"; - hoverlabel: Partial; - hovertemplate: string | string[]; - label: Datum[]; - line: Partial<{ - color: SankeyColor; - width: number; - }>; - pad: number; - thickness: number; - x: number[]; - y: number[]; + color: SankeyColor[]; + customdata: Datum[]; + groups: SankeyNode[]; + hoverinfo: 'all' | 'none' | 'skip'; + hoverlabel: Partial; + hovertemplate: string | string[]; + label: Datum[]; + line: Partial<{ + color: SankeyColor; + width: number; + }>; + pad: number; + thickness: number; + x: number[]; + y: number[]; } export interface SankeyColorscale { - cmax: number; - cmin: number; - colorscale: Array<[number, string]>; - label: string; - name: string; - templateitemname: string; + cmax: number; + cmin: number; + colorscale: Array<[number, string]>; + label: string; + name: string; + templateitemname: string; } export interface SankeyLink { - arrowlen: number; - color: SankeyColor | SankeyColor[]; - colorscale: Partial; - customdata: Datum[]; - hoverinfo: "all" | "none" | "skip"; - hoverlabel: Partial; - hovertemplate: string | string[]; - hovercolor: SankeyColor | SankeyColor[]; - label: Datum[]; - line: Partial<{ - color: SankeyColor; - width: number; - }>; - source: number[]; - target: number[]; - value: number[]; + arrowlen: number; + color: SankeyColor | SankeyColor[]; + colorscale: Partial; + customdata: Datum[]; + hoverinfo: 'all' | 'none' | 'skip'; + hoverlabel: Partial; + hovertemplate: string | string[]; + hovercolor: SankeyColor | SankeyColor[]; + label: Datum[]; + line: Partial<{ + color: SankeyColor; + width: number; + }>; + source: number[]; + target: number[]; + value: number[]; } export interface SankeyData { - type: "sankey"; - name: string; - orientation: SankeyOrientation; - visible: boolean | "legendonly"; - legend: string; - legendrank: number; - legendgrouptitle: Partial; - legendwidth: number; - ids: string[]; - hoverinfo: string; - meta: number | string; - customdata: Datum[]; - domain: Partial; - node: Partial; - link: Partial; - textfont: Partial; - selectpoints: string | number; - arrangement: "snap" | "perpendicular" | "freeform" | "fixed"; - hoverlabel: Partial; - valueformat: string; - valuesuffix: string; - uirevision: string | number; + type: 'sankey'; + name: string; + orientation: SankeyOrientation; + visible: boolean | 'legendonly'; + legend: string; + legendrank: number; + legendgrouptitle: Partial; + legendwidth: number; + ids: string[]; + hoverinfo: string; + meta: number | string; + customdata: Datum[]; + domain: Partial; + node: Partial; + link: Partial; + textfont: Partial; + selectpoints: string | number; + arrangement: 'snap' | 'perpendicular' | 'freeform' | 'fixed'; + hoverlabel: Partial; + valueformat: string; + valuesuffix: string; + uirevision: string | number; } export interface Point { - x: number; - y: number; - z: number; + x: number; + y: number; + z: number; } export interface PlotScatterDataPoint { - curveNumber: number; - data: PlotData; - pointIndex: number; - pointNumber: number; - x: number; - xaxis: LayoutAxis; - y: number; - yaxis: LayoutAxis; + curveNumber: number; + data: PlotData; + pointIndex: number; + pointNumber: number; + x: number; + xaxis: LayoutAxis; + y: number; + yaxis: LayoutAxis; } export interface PlotDatum { - curveNumber: number; - data: PlotData; - customdata: Datum; - pointIndex: number; - pointNumber: number; - x: Datum; - xaxis: LayoutAxis; - y: Datum; - yaxis: LayoutAxis; - text: string; + curveNumber: number; + data: PlotData; + customdata: Datum; + pointIndex: number; + pointNumber: number; + x: Datum; + xaxis: LayoutAxis; + y: Datum; + yaxis: LayoutAxis; + text: string; } export interface PlotCoordinate { - x: number; - y: number; - pointNumber: number; + x: number; + y: number; + pointNumber: number; } export interface SelectionRange { - x: number[]; - y: number[]; + x: number[]; + y: number[]; } export type PlotSelectedData = Partial; export interface PlotScene { - center: Point; - eye: Point; - up: Point; + center: Point; + eye: Point; + up: Point; } export interface PolarLayout { - domain: Partial; - sector: number[]; - hole: number; - bgcolor: Color; - radialaxis: Partial; - angularaxis: Partial; - gridshape: "circular" | "linear"; - uirevision: string | number; - uid: string; + domain: Partial; + sector: number[]; + hole: number; + bgcolor: Color; + radialaxis: Partial; + angularaxis: Partial; + gridshape: 'circular' | 'linear'; + uirevision: string | number; + uid: string; } export interface PlotlySchema { - data: Data[]; - layout?: Partial; - config?: Partial; + data: Data[]; + layout?: Partial; + config?: Partial; } // Layout export interface Layout { - colorway: string[]; - title: - | string - | Partial<{ - text: string; - font: Partial; - xref: "container" | "paper"; - yref: "container" | "paper"; - x: number; - y: number; - xanchor: "auto" | "left" | "center" | "right"; - yanchor: "auto" | "top" | "middle" | "bottom"; - pad: Partial; - }>; - titlefont: Partial; - autosize: boolean; - showlegend: boolean; - paper_bgcolor: Color; - plot_bgcolor: Color; - separators: string; - hidesources: boolean; - xaxis: Partial; - xaxis2: Partial; - xaxis3: Partial; - xaxis4: Partial; - xaxis5: Partial; - xaxis6: Partial; - xaxis7: Partial; - xaxis8: Partial; - xaxis9: Partial; - yaxis: Partial; - yaxis2: Partial; - yaxis3: Partial; - yaxis4: Partial; - yaxis5: Partial; - yaxis6: Partial; - yaxis7: Partial; - yaxis8: Partial; - yaxis9: Partial; - margin: Partial; - height: number; - width: number; - hovermode: "closest" | "x" | "y" | "x unified" | "y unified" | false; - hoverdistance: number; - hoverlabel: Partial; - calendar: Calendar; - "xaxis.range": [Datum, Datum]; - "xaxis.range[0]": Datum; - "xaxis.range[1]": Datum; - "yaxis.range": [Datum, Datum]; - "yaxis.range[0]": Datum; - "yaxis.range[1]": Datum; - "yaxis.type": AxisType; - "xaxis.type": AxisType; - "xaxis.autorange": boolean; - "yaxis.autorange": boolean; - "xaxis.title": string; - "yaxis.title": string; - ternary: any; - geo: any; - mapbox: any; - subplot: string; - radialaxis: Partial; - angularaxis: {}; - dragmode: - | "zoom" - | "pan" - | "select" - | "lasso" - | "drawclosedpath" - | "drawopenpath" - | "drawline" - | "drawrect" - | "drawcircle" - | "orbit" - | "turntable" - | false; - orientation: number; - annotations: Array>; - shapes: Array>; - legend: Partial; - font: Partial; - barmode: "stack" | "group" | "overlay" | "relative"; - barnorm: "" | "fraction" | "percent"; - bargap: number; - bargroupgap: number; - boxmode: "group" | "overlay"; - selectdirection: "h" | "v" | "d" | "any"; - hiddenlabels: string[]; - grid: Partial<{ - rows: number; - roworder: "top to bottom" | "bottom to top"; - columns: number; - subplots: string[]; - xaxes: string[]; - yaxes: string[]; - pattern: "independent" | "coupled"; - xgap: number; - ygap: number; - domain: Partial<{ - x: number[]; - y: number[]; - }>; - xside: "bottom" | "bottom plot" | "top plot" | "top"; - yside: "left" | "left plot" | "right plot" | "right"; + colorway: string[]; + title: + | string + | Partial<{ + text: string; + font: Partial; + xref: 'container' | 'paper'; + yref: 'container' | 'paper'; + x: number; + y: number; + xanchor: 'auto' | 'left' | 'center' | 'right'; + yanchor: 'auto' | 'top' | 'middle' | 'bottom'; + pad: Partial; + }>; + titlefont: Partial; + autosize: boolean; + showlegend: boolean; + paper_bgcolor: Color; + plot_bgcolor: Color; + separators: string; + hidesources: boolean; + xaxis: Partial; + xaxis2: Partial; + xaxis3: Partial; + xaxis4: Partial; + xaxis5: Partial; + xaxis6: Partial; + xaxis7: Partial; + xaxis8: Partial; + xaxis9: Partial; + yaxis: Partial; + yaxis2: Partial; + yaxis3: Partial; + yaxis4: Partial; + yaxis5: Partial; + yaxis6: Partial; + yaxis7: Partial; + yaxis8: Partial; + yaxis9: Partial; + margin: Partial; + height: number; + width: number; + hovermode: 'closest' | 'x' | 'y' | 'x unified' | 'y unified' | false; + hoverdistance: number; + hoverlabel: Partial; + calendar: Calendar; + 'xaxis.range': [Datum, Datum]; + 'xaxis.range[0]': Datum; + 'xaxis.range[1]': Datum; + 'yaxis.range': [Datum, Datum]; + 'yaxis.range[0]': Datum; + 'yaxis.range[1]': Datum; + 'yaxis.type': AxisType; + 'xaxis.type': AxisType; + 'xaxis.autorange': boolean; + 'yaxis.autorange': boolean; + 'xaxis.title': string; + 'yaxis.title': string; + ternary: any; + geo: any; + mapbox: any; + subplot: string; + radialaxis: Partial; + angularaxis: {}; + dragmode: + | 'zoom' + | 'pan' + | 'select' + | 'lasso' + | 'drawclosedpath' + | 'drawopenpath' + | 'drawline' + | 'drawrect' + | 'drawcircle' + | 'orbit' + | 'turntable' + | false; + orientation: number; + annotations: Array>; + shapes: Array>; + legend: Partial; + font: Partial; + barmode: 'stack' | 'group' | 'overlay' | 'relative'; + barnorm: '' | 'fraction' | 'percent'; + bargap: number; + bargroupgap: number; + boxmode: 'group' | 'overlay'; + selectdirection: 'h' | 'v' | 'd' | 'any'; + hiddenlabels: string[]; + grid: Partial<{ + rows: number; + roworder: 'top to bottom' | 'bottom to top'; + columns: number; + subplots: string[]; + xaxes: string[]; + yaxes: string[]; + pattern: 'independent' | 'coupled'; + xgap: number; + ygap: number; + domain: Partial<{ + x: number[]; + y: number[]; }>; - polar: Partial; - polar2: Partial; - polar3: Partial; - polar4: Partial; - polar5: Partial; - polar6: Partial; - polar7: Partial; - polar8: Partial; - polar9: Partial; - template: Template; - clickmode: "event" | "select" | "event+select" | "none"; - uirevision: number | string; - uid: string; - datarevision: number | string; - editrevision: number | string; - selectionrevision: number | string; + xside: 'bottom' | 'bottom plot' | 'top plot' | 'top'; + yside: 'left' | 'left plot' | 'right plot' | 'right'; + }>; + polar: Partial; + polar2: Partial; + polar3: Partial; + polar4: Partial; + polar5: Partial; + polar6: Partial; + polar7: Partial; + polar8: Partial; + polar9: Partial; + template: Template; + clickmode: 'event' | 'select' | 'event+select' | 'none'; + uirevision: number | string; + uid: string; + datarevision: number | string; + editrevision: number | string; + selectionrevision: number | string; } export interface Legend extends Label { - borderwidth: number; - groupclick: "toggleitem" | "togglegroup"; - grouptitlefont: Partial; - itemclick: "toggle" | "toggleothers" | false; - itemdoubleclick: "toggle" | "toggleothers" | false; - itemsizing: "trace" | "constant"; - itemwidth: number; - orientation: "v" | "h"; - title: Partial; - tracegroupgap: number; - traceorder: "grouped" | "normal" | "reversed" | "reversed+grouped"; - uirevision: number | string; - uid: string; - valign: "top" | "middle" | "bottom"; - x: number; - xanchor: "auto" | "left" | "center" | "right"; - xref: "container" | "paper"; - y: number; - yanchor: "auto" | "top" | "middle" | "bottom"; - yref: "container" | "paper"; -} - -export type AxisType = "-" | "linear" | "log" | "date" | "category" | "multicategory"; + borderwidth: number; + groupclick: 'toggleitem' | 'togglegroup'; + grouptitlefont: Partial; + itemclick: 'toggle' | 'toggleothers' | false; + itemdoubleclick: 'toggle' | 'toggleothers' | false; + itemsizing: 'trace' | 'constant'; + itemwidth: number; + orientation: 'v' | 'h'; + title: Partial; + tracegroupgap: number; + traceorder: 'grouped' | 'normal' | 'reversed' | 'reversed+grouped'; + uirevision: number | string; + uid: string; + valign: 'top' | 'middle' | 'bottom'; + x: number; + xanchor: 'auto' | 'left' | 'center' | 'right'; + xref: 'container' | 'paper'; + y: number; + yanchor: 'auto' | 'top' | 'middle' | 'bottom'; + yref: 'container' | 'paper'; +} + +export type AxisType = '-' | 'linear' | 'log' | 'date' | 'category' | 'multicategory'; export type DTickValue = number | string; export interface TickFormatStop { - /** - * Determines whether or not this stop is used. If `false`, - * this stop is ignored even within its `dtickrange`. - */ - enabled: boolean; - /** - * Range [`min`, `max`], where `min`, `max` - dtick values - * which describe some zoom level, it is possible to omit `min` or `max` - * value by passing `null` - */ - dtickrange: [DTickValue | null, DTickValue | null]; - /** - * dtickformat for described zoom level, the same as `tickformat` - */ - value: string; - /** - * When used in a template, named items are created in the output figure - * in addition to any items the figure already has in this array. - * You can modify these items in the output figure by making - * your own item with `templateitemname` matching this `name` - * alongside your modifications (including `visible: false` or `enabled: false` to hide it). - * Has no effect outside of a template. - */ - name: string; - /** - * Used to refer to a named item in this array in the template. - * Named items from the template will be created even without - * a matching item in the input figure, but you can modify one by - * making an item with `templateitemname` matching its `name`, - * alongside your modifications (including `visible: false` or `enabled: false` to hide it). - * If there is no template or no matching item, this item will be hidden - * unless you explicitly show it with `visible: true`. - */ - templateitemname: string; + /** + * Determines whether or not this stop is used. If `false`, + * this stop is ignored even within its `dtickrange`. + */ + enabled: boolean; + /** + * Range [`min`, `max`], where `min`, `max` - dtick values + * which describe some zoom level, it is possible to omit `min` or `max` + * value by passing `null` + */ + dtickrange: [DTickValue | null, DTickValue | null]; + /** + * dtickformat for described zoom level, the same as `tickformat` + */ + value: string; + /** + * When used in a template, named items are created in the output figure + * in addition to any items the figure already has in this array. + * You can modify these items in the output figure by making + * your own item with `templateitemname` matching this `name` + * alongside your modifications (including `visible: false` or `enabled: false` to hide it). + * Has no effect outside of a template. + */ + name: string; + /** + * Used to refer to a named item in this array in the template. + * Named items from the template will be created even without + * a matching item in the input figure, but you can modify one by + * making an item with `templateitemname` matching its `name`, + * alongside your modifications (including `visible: false` or `enabled: false` to hide it). + * If there is no template or no matching item, this item will be hidden + * unless you explicitly show it with `visible: true`. + */ + templateitemname: string; } export interface AutoRangeOptions { - clipmax: DTickValue; - clipmin: DTickValue; - include: DTickValue; - maxallowed: DTickValue; - minallowed: DTickValue; + clipmax: DTickValue; + clipmin: DTickValue; + include: DTickValue; + maxallowed: DTickValue; + minallowed: DTickValue; } export interface MinorAxisLayout { - dtick: DTickValue; - gridcolor: Color; - griddash: Dash; - gridwidth: number; - nticks: number; - showgrid: boolean; - tick0: DTickValue; - tickcolor: Color; - ticklen: number; - tickmode: "auto" | "linear" | "array"; - ticks: "outside" | "inside" | ""; - tickvals: any[]; - tickwidth: number; + dtick: DTickValue; + gridcolor: Color; + griddash: Dash; + gridwidth: number; + nticks: number; + showgrid: boolean; + tick0: DTickValue; + tickcolor: Color; + ticklen: number; + tickmode: 'auto' | 'linear' | 'array'; + ticks: 'outside' | 'inside' | ''; + tickvals: any[]; + tickwidth: number; } export interface RangeBreak { - bounds: any[]; - dvalue: number; - enabled: boolean; - name: string; - pattern: "day of week" | "hour" | ""; - templateitemname: string; - values: any[]; + bounds: any[]; + dvalue: number; + enabled: boolean; + name: string; + pattern: 'day of week' | 'hour' | ''; + templateitemname: string; + values: any[]; } export interface Axis { - /** - * A single toggle to hide the axis while preserving interaction like dragging. - * Default is true when a cheater plot is present on the axis, otherwise - * false - */ - visible: boolean; - /** - * Sets default for all colors associated with this axis - * all at once: line, font, tick, and grid colors. - * Grid color is lightened by blending this with the plot background - * Individual pieces can override this. - */ - color: Color; - title: string | Partial; - /** - * Former `titlefont` is now the sub-attribute `font` of `title`. - * To customize title font properties, please use `title.font` now. - */ - titlefont: Partial; - type: AxisType; - autorange: true | false | "reversed" | "min reversed" | "max reversed" | "min" | "max"; - autorangeoptions: Partial; - /** - * 'If *normal*, the range is computed in relation to the extrema - * of the input data. - * If *tozero*`, the range extends to 0, - * regardless of the input data - * If *nonnegative*, the range is non-negative, - * regardless of the input data. - * Applies only to linear axes. - */ - rangemode: "normal" | "tozero" | "nonnegative"; - range: any[]; - /** - * Determines whether or not this axis is zoom-able. - * If true, then zoom is disabled. - */ - fixedrange: boolean; - - /** - * Ticks - */ - tickmode: "auto" | "linear" | "array"; - nticks: number; - tick0: number | string; - dtick: DTickValue; - tickvals: any[]; - ticktext: string[]; - ticks: "outside" | "inside" | ""; - mirror: true | "ticks" | false | "all" | "allticks"; - ticklen: number; - tickwidth: number; - tickcolor: Color; - showticklabels: boolean; - showspikes: boolean; - spikecolor: Color; - spikethickness: number; - /** - * Specifies the ordering logic for the case of categorical variables. - * By default, plotly uses *trace*, which specifies the order that is present in the data supplied. - * Set `categoryorder` to *category ascending* or *category descending* if order should be determined by - * the alphanumerical order of the category names. - * Set `categoryorder` to *array* to derive the ordering from the attribute `categoryarray`. If a category - * is not found in the `categoryarray` array, the sorting behavior for that attribute will be identical to - * the *trace* mode. The unspecified categories will follow the categories in `categoryarray`. - * Set `categoryorder` to *total ascending* or *total descending* if order should be determined by the - * numerical order of the values. - * Similarly, the order can be determined by the min, max, sum, mean or median of all the values. - */ - categoryorder: - | "trace" - | "category ascending" - | "category descending" - | "array" - | "total ascending" - | "total descending" - | "min ascending" - | "min descending" - | "max ascending" - | "max descending" - | "sum ascending" - | "sum descending" - | "mean ascending" - | "mean descending" - | "median ascending" - | "median descending"; - categoryarray: any[]; - tickfont: Partial; - tickangle: "auto" | number; - tickprefix: string; - /** - * If `all`, all tick labels are displayed with a prefix. - * If `first`, only the first tick is displayed with a prefix. - * If `last`, only the last tick is displayed with a suffix. - * If `none`, tick prefixes are hidden. - */ - showtickprefix: "all" | "first" | "last" | "none"; - /** - * Sets a tick label suffix. - */ - ticksuffix: string; - /** - * Same as `showtickprefix` but for tick suffixes. - */ - showticksuffix: "all" | "first" | "last" | "none"; - /** - * If `all`, all exponents are shown besides their significands. - * If `first`, only the exponent of the first tick is shown. - * If `last`, only the exponent of the last tick is shown. - * If `none`, no exponents appear. - */ - showexponent: "all" | "first" | "last" | "none"; - /** - * Determines a formatting rule for the tick exponents. - * For example, consider the number 1,000,000,000. - * If `none`, it appears as *1,000,000,000*. - * If `e`, *1e+9*. - * If `E`, *1E+9*. - * If `power`, *1x10^9* (with 9 in a super script). - * If `SI`, *1G*. - * If `B`, *1B*. - */ - exponentformat: "none" | "e" | "E" | "power" | "SI" | "B"; - /** - * Hide SI prefix for 10^n if |n| is below this number. This only has an effect when `tickformat` is "SI" or "B". - */ - minexponent: number; - /** - * 'If `true`, even 4-digit integers are separated - */ - separatethousands: boolean; - /** - * Sets the tick label formatting rule using d3 formatting mini-languages - * which are very similar to those in Python. - * For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format - * And for dates see: https://github.com/d3/d3-3.x-api-reference/blob/master/Time-Formatting.md#format - * We add one item to d3's date formatter: `%{n}f` for fractional seconds with n digits. - * For example, `"2016-10-13 09:15:23.456"` with tickformat `"%H~%M~%S.%2f"` would display `"09~15~23.46"` - */ - tickformat: string; - /** - * Sets the hover text formatting rule using d3 formatting mini-languages - * which are very similar to those in Python. - * For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format - * And for dates see: https://github.com/d3/d3-3.x-api-reference/blob/master/Time-Formatting.md#format - * We add one item to d3's date formatter: `%{n}f` for fractional seconds with n digits. - * For example, `"2016-10-13 09:15:23.456"` with tickformat `"%H~%M~%S.%2f"` would display "09~15~23.46" - */ - hoverformat: string; - calendar: Calendar; - /** - * Array of `Partial` objects. - */ - tickformatstops: Array>; - spikedash: string; - /** - * Determines the drawing mode for the spike line. - * If `toaxis`, the line is drawn from the data point to the axis the - * series is plotted on. - * If `across`, the line is drawn across the entire plot area, and - * supercedes *toaxis*. - * If `marker`, then a marker dot is drawn on the axis the series is - * plotted on - */ - spikemode: - | "toaxis" - | "across" - | "marker" - | "toaxis+across" - | "toaxis+across+marker" - | "across+marker" - | "toaxis+marker"; - /** - * Determines whether spikelines are stuck to the cursor or to the closest datapoints. - */ - spikesnap: "data" | "cursor" | "hovered data"; - - /** - * Lines and Grids - */ - - /** - * Determines whether or not a line bounding this axis is drawn. - */ - showline: boolean; - /** - * Sets the axis line color - */ - linecolor: Color; - /** - * Sets the width (in px) of the axis line. - */ - linewidth: number; - /** - * Determines whether or not grid lines are drawn. - * If `true`, the grid lines are drawn at every tick mark. - */ - showgrid: boolean; - /** - * Sets the color of the grid lines. - */ - gridcolor: Color; - /** - * Sets the width (in px) of the grid lines. - */ - gridwidth: number; - /** - * Determines whether or not a line is drawn at along the 0 value - * of this axis. - * If `true`, the zero line is drawn on top of the grid lines. - */ - zeroline: boolean; - /** - * Sets the line color of the zero line. - */ - zerolinecolor: Color; - /** - * Sets the width (in px) of the zero line. - */ - zerolinewidth: number; - /** - * Determines whether or not a dividers are drawn - * between the category levels of this axis. - * Only has an effect on *multicategory* axes. - */ - showdividers: boolean; - /** - * Sets the color of the dividers - * Only has an effect on *multicategory* axes. - */ - dividercolor: Color; - /** - * Sets the width (in px) of the dividers - * Only has an effect on *multicategory* axes. - */ - dividerwidth: number; - - autotypenumbers: "convert types" | "strict"; - labelalias: DTickValue; - maxallowed: DTickValue; - minallowed: DTickValue; + /** + * A single toggle to hide the axis while preserving interaction like dragging. + * Default is true when a cheater plot is present on the axis, otherwise + * false + */ + visible: boolean; + /** + * Sets default for all colors associated with this axis + * all at once: line, font, tick, and grid colors. + * Grid color is lightened by blending this with the plot background + * Individual pieces can override this. + */ + color: Color; + title: string | Partial; + /** + * Former `titlefont` is now the sub-attribute `font` of `title`. + * To customize title font properties, please use `title.font` now. + */ + titlefont: Partial; + type: AxisType; + autorange: true | false | 'reversed' | 'min reversed' | 'max reversed' | 'min' | 'max'; + autorangeoptions: Partial; + /** + * 'If *normal*, the range is computed in relation to the extrema + * of the input data. + * If *tozero*`, the range extends to 0, + * regardless of the input data + * If *nonnegative*, the range is non-negative, + * regardless of the input data. + * Applies only to linear axes. + */ + rangemode: 'normal' | 'tozero' | 'nonnegative'; + range: any[]; + /** + * Determines whether or not this axis is zoom-able. + * If true, then zoom is disabled. + */ + fixedrange: boolean; + + /** + * Ticks + */ + tickmode: 'auto' | 'linear' | 'array'; + nticks: number; + tick0: number | string; + dtick: DTickValue; + tickvals: any[]; + ticktext: string[]; + ticks: 'outside' | 'inside' | ''; + mirror: true | 'ticks' | false | 'all' | 'allticks'; + ticklen: number; + tickwidth: number; + tickcolor: Color; + showticklabels: boolean; + showspikes: boolean; + spikecolor: Color; + spikethickness: number; + /** + * Specifies the ordering logic for the case of categorical variables. + * By default, plotly uses *trace*, which specifies the order that is present in the data supplied. + * Set `categoryorder` to *category ascending* or *category descending* if order should be determined by + * the alphanumerical order of the category names. + * Set `categoryorder` to *array* to derive the ordering from the attribute `categoryarray`. If a category + * is not found in the `categoryarray` array, the sorting behavior for that attribute will be identical to + * the *trace* mode. The unspecified categories will follow the categories in `categoryarray`. + * Set `categoryorder` to *total ascending* or *total descending* if order should be determined by the + * numerical order of the values. + * Similarly, the order can be determined by the min, max, sum, mean or median of all the values. + */ + categoryorder: + | 'trace' + | 'category ascending' + | 'category descending' + | 'array' + | 'total ascending' + | 'total descending' + | 'min ascending' + | 'min descending' + | 'max ascending' + | 'max descending' + | 'sum ascending' + | 'sum descending' + | 'mean ascending' + | 'mean descending' + | 'median ascending' + | 'median descending'; + categoryarray: any[]; + tickfont: Partial; + tickangle: 'auto' | number; + tickprefix: string; + /** + * If `all`, all tick labels are displayed with a prefix. + * If `first`, only the first tick is displayed with a prefix. + * If `last`, only the last tick is displayed with a suffix. + * If `none`, tick prefixes are hidden. + */ + showtickprefix: 'all' | 'first' | 'last' | 'none'; + /** + * Sets a tick label suffix. + */ + ticksuffix: string; + /** + * Same as `showtickprefix` but for tick suffixes. + */ + showticksuffix: 'all' | 'first' | 'last' | 'none'; + /** + * If `all`, all exponents are shown besides their significands. + * If `first`, only the exponent of the first tick is shown. + * If `last`, only the exponent of the last tick is shown. + * If `none`, no exponents appear. + */ + showexponent: 'all' | 'first' | 'last' | 'none'; + /** + * Determines a formatting rule for the tick exponents. + * For example, consider the number 1,000,000,000. + * If `none`, it appears as *1,000,000,000*. + * If `e`, *1e+9*. + * If `E`, *1E+9*. + * If `power`, *1x10^9* (with 9 in a super script). + * If `SI`, *1G*. + * If `B`, *1B*. + */ + exponentformat: 'none' | 'e' | 'E' | 'power' | 'SI' | 'B'; + /** + * Hide SI prefix for 10^n if |n| is below this number. This only has an effect when `tickformat` is "SI" or "B". + */ + minexponent: number; + /** + * 'If `true`, even 4-digit integers are separated + */ + separatethousands: boolean; + /** + * Sets the tick label formatting rule using d3 formatting mini-languages + * which are very similar to those in Python. + * For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format + * And for dates see: https://github.com/d3/d3-3.x-api-reference/blob/master/Time-Formatting.md#format + * We add one item to d3's date formatter: `%{n}f` for fractional seconds with n digits. + * For example, `"2016-10-13 09:15:23.456"` with tickformat `"%H~%M~%S.%2f"` would display `"09~15~23.46"` + */ + tickformat: string; + /** + * Sets the hover text formatting rule using d3 formatting mini-languages + * which are very similar to those in Python. + * For numbers, see: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format + * And for dates see: https://github.com/d3/d3-3.x-api-reference/blob/master/Time-Formatting.md#format + * We add one item to d3's date formatter: `%{n}f` for fractional seconds with n digits. + * For example, `"2016-10-13 09:15:23.456"` with tickformat `"%H~%M~%S.%2f"` would display "09~15~23.46" + */ + hoverformat: string; + calendar: Calendar; + /** + * Array of `Partial` objects. + */ + tickformatstops: Array>; + spikedash: string; + /** + * Determines the drawing mode for the spike line. + * If `toaxis`, the line is drawn from the data point to the axis the + * series is plotted on. + * If `across`, the line is drawn across the entire plot area, and + * supercedes *toaxis*. + * If `marker`, then a marker dot is drawn on the axis the series is + * plotted on + */ + spikemode: + | 'toaxis' + | 'across' + | 'marker' + | 'toaxis+across' + | 'toaxis+across+marker' + | 'across+marker' + | 'toaxis+marker'; + /** + * Determines whether spikelines are stuck to the cursor or to the closest datapoints. + */ + spikesnap: 'data' | 'cursor' | 'hovered data'; + + /** + * Lines and Grids + */ + + /** + * Determines whether or not a line bounding this axis is drawn. + */ + showline: boolean; + /** + * Sets the axis line color + */ + linecolor: Color; + /** + * Sets the width (in px) of the axis line. + */ + linewidth: number; + /** + * Determines whether or not grid lines are drawn. + * If `true`, the grid lines are drawn at every tick mark. + */ + showgrid: boolean; + /** + * Sets the color of the grid lines. + */ + gridcolor: Color; + /** + * Sets the width (in px) of the grid lines. + */ + gridwidth: number; + /** + * Determines whether or not a line is drawn at along the 0 value + * of this axis. + * If `true`, the zero line is drawn on top of the grid lines. + */ + zeroline: boolean; + /** + * Sets the line color of the zero line. + */ + zerolinecolor: Color; + /** + * Sets the width (in px) of the zero line. + */ + zerolinewidth: number; + /** + * Determines whether or not a dividers are drawn + * between the category levels of this axis. + * Only has an effect on *multicategory* axes. + */ + showdividers: boolean; + /** + * Sets the color of the dividers + * Only has an effect on *multicategory* axes. + */ + dividercolor: Color; + /** + * Sets the width (in px) of the dividers + * Only has an effect on *multicategory* axes. + */ + dividerwidth: number; + + autotypenumbers: 'convert types' | 'strict'; + labelalias: DTickValue; + maxallowed: DTickValue; + minallowed: DTickValue; } export type Calendar = - | "gregorian" - | "chinese" - | "coptic" - | "discworld" - | "ethiopian" - | "hebrew" - | "islamic" - | "julian" - | "mayan" - | "nanakshahi" - | "nepali" - | "persian" - | "jalali" - | "taiwan" - | "thai" - | "ummalqura"; + | 'gregorian' + | 'chinese' + | 'coptic' + | 'discworld' + | 'ethiopian' + | 'hebrew' + | 'islamic' + | 'julian' + | 'mayan' + | 'nanakshahi' + | 'nepali' + | 'persian' + | 'jalali' + | 'taiwan' + | 'thai' + | 'ummalqura'; // regex from documentation: "/^x([2-9]|[1-9][0-9]+)?( domain)?$/" | "/^y([2-9]|[1-9][0-9]+)?( domain)?$/" -// regex allows for an unlimited amount of digits for the 'axis number', but the following typescript definition is limited to two digits +// regex allows for an unlimited amount of digits for the 'axis number', +// but the following typescript definition is limited to two digits type xYAxisNames = `${ - | "" - | `${2 | 3 | 4 | 5 | 6 | 7 | 8 | 9}` - | `${1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9}${0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9}`}${"" | " domain"}`; + | '' + | `${2 | 3 | 4 | 5 | 6 | 7 | 8 | 9}` + | `${1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9}${0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9}`}${'' | ' domain'}`; export type XAxisName = `x${xYAxisNames}`; export type YAxisName = `y${xYAxisNames}`; @@ -792,531 +795,527 @@ export type YAxisName = `y${xYAxisNames}`; export type AxisName = XAxisName | YAxisName; export interface LayoutAxis extends Axis { - fixedrange: boolean; - scaleanchor: AxisName; - scaleratio: number; - constrain: "range" | "domain"; - constraintoward: "left" | "center" | "right" | "top" | "middle" | "bottom"; - anchor: "free" | AxisName; - side: "top" | "bottom" | "left" | "right" | "clockwise" | "counterclockwise"; - overlaying: "free" | AxisName; - layer: "above traces" | "below traces"; - domain: number[]; - position: number; - rotation: number; - direction: "counterclockwise" | "clockwise"; - rangeslider: Partial; - rangeselector: Partial; - automargin: boolean; - autotick: boolean; - angle: any; - griddash: Dash; - l2p: (v: Datum) => number; - - autotickangles: number[]; - insiderange: any[]; - matches: AxisName; - minor: Partial; - rangebreaks: Array>; - ticklabelmode: "instant" | "period"; - ticklabeloverflow: "allow" | "hide past div" | "hide past domain"; - ticklabelposition: - | "outside" - | "inside" - | "outside top" - | "inside top" - | "outside left" - | "inside left" - | "outside right" - | "inside right" - | "outside bottom" - | "inside bottom"; - ticklabelstep: number; - tickson: "labels" | "boundaries"; - uirevision: DTickValue; + fixedrange: boolean; + scaleanchor: AxisName; + scaleratio: number; + constrain: 'range' | 'domain'; + constraintoward: 'left' | 'center' | 'right' | 'top' | 'middle' | 'bottom'; + anchor: 'free' | AxisName; + side: 'top' | 'bottom' | 'left' | 'right' | 'clockwise' | 'counterclockwise'; + overlaying: 'free' | AxisName; + layer: 'above traces' | 'below traces'; + domain: number[]; + position: number; + rotation: number; + direction: 'counterclockwise' | 'clockwise'; + rangeslider: Partial; + rangeselector: Partial; + automargin: boolean; + autotick: boolean; + angle: any; + griddash: Dash; + l2p: (v: Datum) => number; + + autotickangles: number[]; + insiderange: any[]; + matches: AxisName; + minor: Partial; + rangebreaks: Array>; + ticklabelmode: 'instant' | 'period'; + ticklabeloverflow: 'allow' | 'hide past div' | 'hide past domain'; + ticklabelposition: + | 'outside' + | 'inside' + | 'outside top' + | 'inside top' + | 'outside left' + | 'inside left' + | 'outside right' + | 'inside right' + | 'outside bottom' + | 'inside bottom'; + ticklabelstep: number; + tickson: 'labels' | 'boundaries'; + uirevision: DTickValue; } export interface SceneAxis extends Axis { - spikesides: boolean; - showbackground: boolean; - backgroundcolor: Color; - showaxeslabels: boolean; + spikesides: boolean; + showbackground: boolean; + backgroundcolor: Color; + showaxeslabels: boolean; } export interface ShapeLine { - color: string; - width: number; - dash: Dash; + color: string; + width: number; + dash: Dash; } export interface ShapeLabel { - font: Partial; - padding: number; - text: string; - textangle: "auto" | number; - textposition: - | "top left" - | "top center" - | "top right" - | "middle left" - | "middle center" - | "middle right" - | "bottom left" - | "bottom center" - | "bottom right" - | "start" - | "middle" - | "end"; - texttemplate: string; - xanchor: "auto" | "left" | "center" | "right"; - yanchor: "top" | "middle" | "bottom"; + font: Partial; + padding: number; + text: string; + textangle: 'auto' | number; + textposition: + | 'top left' + | 'top center' + | 'top right' + | 'middle left' + | 'middle center' + | 'middle right' + | 'bottom left' + | 'bottom center' + | 'bottom right' + | 'start' + | 'middle' + | 'end'; + texttemplate: string; + xanchor: 'auto' | 'left' | 'center' | 'right'; + yanchor: 'top' | 'middle' | 'bottom'; } export interface Shape { - visible: boolean | "legendonly"; - layer: "below" | "above"; - type: "rect" | "circle" | "line" | "path"; - path: string; - xref: "paper" | XAxisName; - xsizemode: "scaled" | "pixel"; - xanchor: number | string; - yref: "paper" | YAxisName; - ysizemode: "scaled" | "pixel"; - yanchor: number | string; - x0: Datum; - y0: Datum; - x1: Datum; - y1: Datum; - fillcolor: string; - name: string; - templateitemname: string; - opacity: number; - line: Partial; - label: Partial; - showlegend: boolean; - legendgroup: string; - legendgrouptitle: { - text: string; - font?: Partial; - }; - legendrank: number; + visible: boolean | 'legendonly'; + layer: 'below' | 'above'; + type: 'rect' | 'circle' | 'line' | 'path'; + path: string; + xref: 'paper' | XAxisName; + xsizemode: 'scaled' | 'pixel'; + xanchor: number | string; + yref: 'paper' | YAxisName; + ysizemode: 'scaled' | 'pixel'; + yanchor: number | string; + x0: Datum; + y0: Datum; + x1: Datum; + y1: Datum; + fillcolor: string; + name: string; + templateitemname: string; + opacity: number; + line: Partial; + label: Partial; + showlegend: boolean; + legendgroup: string; + legendgrouptitle: { + text: string; + font?: Partial; + }; + legendrank: number; } export interface Margin { - t: number; - b: number; - l: number; - r: number; - pad: number; + t: number; + b: number; + l: number; + r: number; + pad: number; } export interface Icon { - height?: number | undefined; - width?: number | undefined; - ascent?: number | undefined; - descent?: number | undefined; - name?: string | undefined; - path?: string | undefined; - svg?: string | undefined; - transform?: string | undefined; + height?: number | undefined; + width?: number | undefined; + ascent?: number | undefined; + descent?: number | undefined; + name?: string | undefined; + path?: string | undefined; + svg?: string | undefined; + transform?: string | undefined; } export interface GaugeLine { - color: Color; - width: number; + color: Color; + width: number; } export interface Threshold { - line: Partial; - value: number; - thickness: number; + line: Partial; + value: number; + thickness: number; } export interface GaugeBar { - color: Color; - line: Partial; - thickness: number; + color: Color; + line: Partial; + thickness: number; } export interface Gauge { - shape: "angular" | "bullet"; - bar: Partial; - bgcolor: Color; - bordercolor: Color; - borderwidth: number; - axis: Partial; - steps: Array<{ range: number[]; color: Color }>; - threshold: Partial; + shape: 'angular' | 'bullet'; + bar: Partial; + bgcolor: Color; + bordercolor: Color; + borderwidth: number; + axis: Partial; + steps: Array<{ range: number[]; color: Color }>; + threshold: Partial; } export interface Delta { - reference: number; - position: "top" | "bottom" | "left" | "right"; - relative: boolean; - valueformat: string; - increasing: { - symbol: string; - color: Color; - }; - decreasing: { - symbol: string; - color: Color; - }; + reference: number; + position: 'top' | 'bottom' | 'left' | 'right'; + relative: boolean; + valueformat: string; + increasing: { + symbol: string; + color: Color; + }; + decreasing: { + symbol: string; + color: Color; + }; } export interface DataTitle { - text: string; - font: Partial; - standoff: number; - position: - | "top left" - | "top center" - | "top right" - | "middle center" - | "bottom left" - | "bottom center" - | "bottom right"; + text: string; + font: Partial; + standoff: number; + position: + | 'top left' + | 'top center' + | 'top right' + | 'middle center' + | 'bottom left' + | 'bottom center' + | 'bottom right'; } export interface PlotNumber { - valueformat: string; - font: Partial; - prefix: string; - suffix: string; + valueformat: string; + font: Partial; + prefix: string; + suffix: string; } export interface Template { - data?: { [type in PlotType]?: Array> } | undefined; - layout?: Partial | undefined; + data?: { [type in PlotType]?: Array> } | undefined; + layout?: Partial | undefined; } // Data export type Datum = string | number | Date | null; export type TypedArray = - | Int8Array - | Uint8Array - | Int16Array - | Uint16Array - | Int32Array - | Uint32Array - | Uint8ClampedArray - | Float32Array - | Float64Array; + | Int8Array + | Uint8Array + | Int16Array + | Uint16Array + | Int32Array + | Uint32Array + | Uint8ClampedArray + | Float32Array + | Float64Array; export interface ErrorOptions { - visible: boolean; - symmetric: boolean; - color: Color; - thickness: number; - width: number; - opacity: number; -} - -export type ErrorBar = - & Partial - & ( - | { - type: "constant" | "percent"; - value: number; - valueminus?: number | undefined; - } - | { - type: "data"; - array: Datum[]; - arrayminus?: Datum[] | undefined; - } - ); - -export type Dash = "solid" | "dot" | "dash" | "longdash" | "dashdot" | "longdashdot"; + visible: boolean; + symmetric: boolean; + color: Color; + thickness: number; + width: number; + opacity: number; +} + +export type ErrorBar = Partial & + ( + | { + type: 'constant' | 'percent'; + value: number; + valueminus?: number | undefined; + } + | { + type: 'data'; + array: Datum[]; + arrayminus?: Datum[] | undefined; + } + ); + +export type Dash = 'solid' | 'dot' | 'dash' | 'longdash' | 'dashdot' | 'longdashdot'; export type PlotType = - | "bar" - | "barpolar" - | "box" - | "candlestick" - | "carpet" - | "choropleth" - | "choroplethmapbox" - | "cone" - | "contour" - | "contourcarpet" - | "densitymapbox" - | "funnel" - | "funnelarea" - | "heatmap" - | "heatmapgl" - | "histogram" - | "histogram2d" - | "histogram2dcontour" - | "image" - | "indicator" - | "isosurface" - | "mesh3d" - | "ohlc" - | "parcats" - | "parcoords" - | "pie" - | "pointcloud" - | "sankey" - | "scatter" - | "scatter3d" - | "scattercarpet" - | "scattergeo" - | "scattergl" - | "scattermapbox" - | "scatterpolar" - | "scatterpolargl" - | "scatterternary" - | "splom" - | "streamtube" - | "sunburst" - | "surface" - | "table" - | "treemap" - | "violin" - | "volume" - | "waterfall"; - -export type Data = - | Partial - | Partial - | Partial; + | 'bar' + | 'barpolar' + | 'box' + | 'candlestick' + | 'carpet' + | 'choropleth' + | 'choroplethmapbox' + | 'cone' + | 'contour' + | 'contourcarpet' + | 'densitymapbox' + | 'funnel' + | 'funnelarea' + | 'heatmap' + | 'heatmapgl' + | 'histogram' + | 'histogram2d' + | 'histogram2dcontour' + | 'image' + | 'indicator' + | 'isosurface' + | 'mesh3d' + | 'ohlc' + | 'parcats' + | 'parcoords' + | 'pie' + | 'pointcloud' + | 'sankey' + | 'scatter' + | 'scatter3d' + | 'scattercarpet' + | 'scattergeo' + | 'scattergl' + | 'scattermapbox' + | 'scatterpolar' + | 'scatterpolargl' + | 'scatterternary' + | 'splom' + | 'streamtube' + | 'sunburst' + | 'surface' + | 'table' + | 'treemap' + | 'violin' + | 'volume' + | 'waterfall'; + +export type Data = Partial | Partial | Partial; export type Color = - | string - | number - | Array - | Array>; + | string + | number + | Array + | Array>; export type ColorScale = string | string[] | Array<[number, string]>; export type DataTransform = Partial; export type ScatterData = PlotData; // Bar Scatter export interface PlotData { - type: PlotType; - x: Datum[] | Datum[][] | TypedArray; - y: Datum[] | Datum[][] | TypedArray; - z: Datum[] | Datum[][] | Datum[][][] | TypedArray; - i: TypedArray; - j: TypedArray; - k: TypedArray; - xy: Float32Array; - error_x: ErrorBar; - error_y: ErrorBar; - xaxis: string; - yaxis: string; - text: string | string[]; - lat: Datum[]; - lon: Datum[]; - line: Partial; - "line.color": Color; - "line.width": number; - "line.dash": Dash; - "line.shape": "linear" | "spline" | "hv" | "vh" | "hvh" | "vhv"; - "line.smoothing": number; - "line.simplify": boolean; - marker: Partial; - "marker.symbol": MarkerSymbol | MarkerSymbol[]; - "marker.color": Color; - "marker.colorscale": ColorScale | ColorScale[]; - "marker.opacity": number | number[]; - "marker.size": number | number[] | number[][]; - "marker.maxdisplayed": number; - "marker.sizeref": number; - "marker.sizemax": number; - "marker.sizemin": number; - "marker.sizemode": "diameter" | "area"; - "marker.showscale": boolean; - "marker.line": Partial; - "marker.line.color": Color; - "marker.line.colorscale": ColorScale | ColorScale[]; - "marker.colorbar": {}; // TODO - "marker.pad.t": number; - "marker.pad.b": number; - "marker.pad.l": number; - "marker.pad.r": number; - mode: - | "lines" - | "markers" - | "text" - | "lines+markers" - | "text+markers" - | "text+lines" - | "text+lines+markers" - | "none" - | "gauge" - | "number" - | "delta" - | "number+delta" - | "gauge+number" - | "gauge+number+delta" - | "gauge+delta"; - histfunc: "count" | "sum" | "avg" | "min" | "max"; - histnorm: "" | "percent" | "probability" | "density" | "probability density"; - hoveron: "points" | "fills"; - hoverinfo: - | "all" - | "name" - | "none" - | "skip" - | "text" - | "x" - | "x+text" - | "x+name" - | "x+y" - | "x+y+text" - | "x+y+name" - | "x+y+z" - | "x+y+z+text" - | "x+y+z+name" - | "y" - | "y+name" - | "y+x" - | "y+text" - | "y+x+text" - | "y+x+name" - | "y+z" - | "y+z+text" - | "y+z+name" - | "y+x+z" - | "y+x+z+text" - | "y+x+z+name" - | "z" - | "z+x" - | "z+x+text" - | "z+x+name" - | "z+y+x" - | "z+y+x+text" - | "z+y+x+name" - | "z+x+y" - | "z+x+y+text" - | "z+x+y+name"; - hoverlabel: Partial; - hovertemplate: string | string[]; - hovertext: string | string[]; - hoverongaps: boolean; - xhoverformat: string; - yhoverformat: string; - zhoverformat: string; - texttemplate: string | string[]; - textinfo: - | "label" - | "label+text" - | "label+value" - | "label+percent" - | "label+text+value" - | "label+text+percent" - | "label+value+percent" - | "text" - | "text+value" - | "text+percent" - | "text+value+percent" - | "value" - | "value+percent" - | "percent" - | "none"; - textposition: - | "top left" - | "top center" - | "top right" - | "middle left" - | "middle center" - | "middle right" - | "bottom left" - | "bottom center" - | "bottom right" - | "inside" - | "outside" - | "auto" - | "none"; - textfont: Partial; - textangle: "auto" | number; - insidetextanchor: "end" | "middle" | "start"; - constraintext: "inside" | "outside" | "both" | "none"; - fill: "none" | "tozeroy" | "tozerox" | "tonexty" | "tonextx" | "toself" | "tonext"; - fillcolor: string; - fillpattern: Partial; - showlegend: boolean; - legendgroup: string; - legendgrouptitle: { - text: string; - font?: Partial; - }; - legendrank: number; - parents: string[]; - name: string; - stackgroup: string; - groupnorm: "" | "fraction" | "percent"; - stackgaps: "infer zero" | "interpolate"; - connectgaps: boolean; - visible: boolean | "legendonly"; - delta: Partial; - gauge: Partial; - number: Partial; - transforms: DataTransform[]; - orientation: "v" | "h"; - width: number | number[]; - boxmean: boolean | "sd"; - boxpoints: "all" | "outliers" | "suspectedoutliers" | false; - jitter: number; - pointpos: number; - opacity: number; - showscale: boolean; - colorscale: ColorScale; - zsmooth: "fast" | "best" | false; - zmin: number; - zmax: number; - ygap: number; - xgap: number; - transpose: boolean; - autobinx: boolean; - xbins: { - start: number | string; - end: number | string; - size: number | string; - }; - value: number; - values: Datum[]; - labels: Datum[]; - direction: "clockwise" | "counterclockwise"; - hole: number; - rotation: number; - theta: Datum[]; - r: Datum[]; - customdata: Datum[] | Datum[][]; - selectedpoints: Datum[]; - domain: Partial<{ - row: number; - column: number; - x: number[]; - y: number[]; - }>; - title: Partial; - branchvalues: "total" | "remainder"; - ids: string[]; - level: string; - cliponaxis: boolean; - automargin: boolean; - locationmode: "ISO-3" | "USA-states" | "country names" | "geojson-id"; - locations: Datum[]; - reversescale: boolean; - colorbar: Partial; - offset: number | number[]; - contours: Partial<{ - coloring: "fill" | "heatmap" | "lines" | "none"; - end: number; - labelfont: Partial; - labelformat: string; - operation: "=" | "<" | ">=" | ">" | "<=" | "[]" | "()" | "[)" | "(]" | "][" | ")(" | "](" | ")["; - showlabels: boolean; - showlines: boolean; - size: number; - start: number; - type: "levels" | "constraint"; - value: number | [lowerBound: number, upperBound: number]; - }>; - autocontour: boolean; - ncontours: number; - uirevision: string | number; - uid: string; + type: PlotType; + x: Datum[] | Datum[][] | TypedArray; + y: Datum[] | Datum[][] | TypedArray; + z: Datum[] | Datum[][] | Datum[][][] | TypedArray; + i: TypedArray; + j: TypedArray; + k: TypedArray; + xy: Float32Array; + error_x: ErrorBar; + error_y: ErrorBar; + xaxis: string; + yaxis: string; + text: string | string[]; + lat: Datum[]; + lon: Datum[]; + line: Partial; + 'line.color': Color; + 'line.width': number; + 'line.dash': Dash; + 'line.shape': 'linear' | 'spline' | 'hv' | 'vh' | 'hvh' | 'vhv'; + 'line.smoothing': number; + 'line.simplify': boolean; + marker: Partial; + 'marker.symbol': MarkerSymbol | MarkerSymbol[]; + 'marker.color': Color; + 'marker.colorscale': ColorScale | ColorScale[]; + 'marker.opacity': number | number[]; + 'marker.size': number | number[] | number[][]; + 'marker.maxdisplayed': number; + 'marker.sizeref': number; + 'marker.sizemax': number; + 'marker.sizemin': number; + 'marker.sizemode': 'diameter' | 'area'; + 'marker.showscale': boolean; + 'marker.line': Partial; + 'marker.line.color': Color; + 'marker.line.colorscale': ColorScale | ColorScale[]; + 'marker.colorbar': {}; // TODO + 'marker.pad.t': number; + 'marker.pad.b': number; + 'marker.pad.l': number; + 'marker.pad.r': number; + mode: + | 'lines' + | 'markers' + | 'text' + | 'lines+markers' + | 'text+markers' + | 'text+lines' + | 'text+lines+markers' + | 'none' + | 'gauge' + | 'number' + | 'delta' + | 'number+delta' + | 'gauge+number' + | 'gauge+number+delta' + | 'gauge+delta'; + histfunc: 'count' | 'sum' | 'avg' | 'min' | 'max'; + histnorm: '' | 'percent' | 'probability' | 'density' | 'probability density'; + hoveron: 'points' | 'fills'; + hoverinfo: + | 'all' + | 'name' + | 'none' + | 'skip' + | 'text' + | 'x' + | 'x+text' + | 'x+name' + | 'x+y' + | 'x+y+text' + | 'x+y+name' + | 'x+y+z' + | 'x+y+z+text' + | 'x+y+z+name' + | 'y' + | 'y+name' + | 'y+x' + | 'y+text' + | 'y+x+text' + | 'y+x+name' + | 'y+z' + | 'y+z+text' + | 'y+z+name' + | 'y+x+z' + | 'y+x+z+text' + | 'y+x+z+name' + | 'z' + | 'z+x' + | 'z+x+text' + | 'z+x+name' + | 'z+y+x' + | 'z+y+x+text' + | 'z+y+x+name' + | 'z+x+y' + | 'z+x+y+text' + | 'z+x+y+name'; + hoverlabel: Partial; + hovertemplate: string | string[]; + hovertext: string | string[]; + hoverongaps: boolean; + xhoverformat: string; + yhoverformat: string; + zhoverformat: string; + texttemplate: string | string[]; + textinfo: + | 'label' + | 'label+text' + | 'label+value' + | 'label+percent' + | 'label+text+value' + | 'label+text+percent' + | 'label+value+percent' + | 'text' + | 'text+value' + | 'text+percent' + | 'text+value+percent' + | 'value' + | 'value+percent' + | 'percent' + | 'none'; + textposition: + | 'top left' + | 'top center' + | 'top right' + | 'middle left' + | 'middle center' + | 'middle right' + | 'bottom left' + | 'bottom center' + | 'bottom right' + | 'inside' + | 'outside' + | 'auto' + | 'none'; + textfont: Partial; + textangle: 'auto' | number; + insidetextanchor: 'end' | 'middle' | 'start'; + constraintext: 'inside' | 'outside' | 'both' | 'none'; + fill: 'none' | 'tozeroy' | 'tozerox' | 'tonexty' | 'tonextx' | 'toself' | 'tonext'; + fillcolor: string; + fillpattern: Partial; + showlegend: boolean; + legendgroup: string; + legendgrouptitle: { + text: string; + font?: Partial; + }; + legendrank: number; + parents: string[]; + name: string; + stackgroup: string; + groupnorm: '' | 'fraction' | 'percent'; + stackgaps: 'infer zero' | 'interpolate'; + connectgaps: boolean; + visible: boolean | 'legendonly'; + delta: Partial; + gauge: Partial; + number: Partial; + transforms: DataTransform[]; + orientation: 'v' | 'h'; + width: number | number[]; + boxmean: boolean | 'sd'; + boxpoints: 'all' | 'outliers' | 'suspectedoutliers' | false; + jitter: number; + pointpos: number; + opacity: number; + showscale: boolean; + colorscale: ColorScale; + zsmooth: 'fast' | 'best' | false; + zmin: number; + zmax: number; + ygap: number; + xgap: number; + transpose: boolean; + autobinx: boolean; + xbins: { + start: number | string; + end: number | string; + size: number | string; + }; + value: number; + values: Datum[]; + labels: Datum[]; + direction: 'clockwise' | 'counterclockwise'; + hole: number; + rotation: number; + theta: Datum[]; + r: Datum[]; + customdata: Datum[] | Datum[][]; + selectedpoints: Datum[]; + domain: Partial<{ + row: number; + column: number; + x: number[]; + y: number[]; + }>; + title: Partial; + branchvalues: 'total' | 'remainder'; + ids: string[]; + level: string; + cliponaxis: boolean; + automargin: boolean; + locationmode: 'ISO-3' | 'USA-states' | 'country names' | 'geojson-id'; + locations: Datum[]; + reversescale: boolean; + colorbar: Partial; + offset: number | number[]; + contours: Partial<{ + coloring: 'fill' | 'heatmap' | 'lines' | 'none'; + end: number; + labelfont: Partial; + labelformat: string; + operation: '=' | '<' | '>=' | '>' | '<=' | '[]' | '()' | '[)' | '(]' | '][' | ')(' | '](' | ')['; + showlabels: boolean; + showlines: boolean; + size: number; + start: number; + type: 'levels' | 'constraint'; + value: number | [lowerBound: number, upperBound: number]; + }>; + autocontour: boolean; + ncontours: number; + uirevision: string | number; + uid: string; } /** @@ -1324,87 +1323,75 @@ export interface PlotData { * https://github.com/plotly/plotly.js/tree/9d6144304308fc3007f0facf2535d38ea3e9b26c/src/transforms */ export interface TransformStyle { - target: number | string | number[] | string[]; - value: Partial; + target: number | string | number[] | string[]; + value: Partial; } export interface TransformAggregation { - target: string; - func?: - | "count" - | "sum" - | "avg" - | "median" - | "mode" - | "rms" - | "stddev" - | "min" - | "max" - | "first" - | "last" - | undefined; - funcmode?: "sample" | "population" | undefined; - enabled?: boolean | undefined; + target: string; + func?: 'count' | 'sum' | 'avg' | 'median' | 'mode' | 'rms' | 'stddev' | 'min' | 'max' | 'first' | 'last' | undefined; + funcmode?: 'sample' | 'population' | undefined; + enabled?: boolean | undefined; } export interface Transform { - type: "aggregate" | "filter" | "groupby" | "sort"; - enabled: boolean; - target: number | string | number[] | string[]; - operation: string; - aggregations: TransformAggregation[]; - preservegaps: boolean; - groups: string | number[] | string[]; - nameformat: string; - styles: TransformStyle[]; - value: any; - order: "ascending" | "descending"; + type: 'aggregate' | 'filter' | 'groupby' | 'sort'; + enabled: boolean; + target: number | string | number[] | string[]; + operation: string; + aggregations: TransformAggregation[]; + preservegaps: boolean; + groups: string | number[] | string[]; + nameformat: string; + styles: TransformStyle[]; + value: any; + order: 'ascending' | 'descending'; } export interface ColorBar { - thicknessmode: "fraction" | "pixels"; - thickness: number; - lenmode: "fraction" | "pixels"; - len: number; - x: number; - xanchor: "left" | "center" | "right"; - xpad: number; - y: number; - yanchor: "top" | "middle" | "bottom"; - ypad: number; - outlinecolor: Color; - outlinewidth: number; - bordercolor: Color; - borderwidth: Color; - bgcolor: Color; - tickmode: "auto" | "linear" | "array"; - nticks: number; - tick0: number | string; - dtick: DTickValue; - tickvals: Datum[] | Datum[][] | Datum[][][] | TypedArray; - ticktext: Datum[] | Datum[][] | Datum[][][] | TypedArray; - ticks: "outside" | "inside" | ""; - ticklen: number; - tickwidth: number; - tickcolor: Color; - showticklabels: boolean; - tickfont: Font; - tickangle: "auto" | number; - tickformat: string; - tickformatstops: Array>; - tickprefix: string; - showtickprefix: "all" | "first" | "last" | "none"; - ticksuffix: string; - showticksuffix: "all" | "first" | "last" | "none"; - separatethousands: boolean; - exponentformat: "none" | "e" | "E" | "power" | "SI" | "B"; - showexponent: "all" | "first" | "last" | "none"; - minexponent: number; - title: string; - titlefont: Font; - titleside: "right" | "top" | "bottom"; - tickvalssrc: any; - ticktextsrc: any; + thicknessmode: 'fraction' | 'pixels'; + thickness: number; + lenmode: 'fraction' | 'pixels'; + len: number; + x: number; + xanchor: 'left' | 'center' | 'right'; + xpad: number; + y: number; + yanchor: 'top' | 'middle' | 'bottom'; + ypad: number; + outlinecolor: Color; + outlinewidth: number; + bordercolor: Color; + borderwidth: Color; + bgcolor: Color; + tickmode: 'auto' | 'linear' | 'array'; + nticks: number; + tick0: number | string; + dtick: DTickValue; + tickvals: Datum[] | Datum[][] | Datum[][][] | TypedArray; + ticktext: Datum[] | Datum[][] | Datum[][][] | TypedArray; + ticks: 'outside' | 'inside' | ''; + ticklen: number; + tickwidth: number; + tickcolor: Color; + showticklabels: boolean; + tickfont: Font; + tickangle: 'auto' | number; + tickformat: string; + tickformatstops: Array>; + tickprefix: string; + showtickprefix: 'all' | 'first' | 'last' | 'none'; + ticksuffix: string; + showticksuffix: 'all' | 'first' | 'last' | 'none'; + separatethousands: boolean; + exponentformat: 'none' | 'e' | 'E' | 'power' | 'SI' | 'B'; + showexponent: 'all' | 'first' | 'last' | 'none'; + minexponent: number; + title: string; + titlefont: Font; + titleside: 'right' | 'top' | 'bottom'; + tickvalssrc: any; + ticktextsrc: any; } export type MarkerSymbol = string | number | Array; @@ -1415,536 +1402,536 @@ export type MarkerSymbol = string | number | Array; * default: "all" */ export interface PlotMarker { - symbol: MarkerSymbol; - color?: Color | Color[] | undefined; - colors?: Color[] | undefined; - colorscale?: ColorScale | undefined; - cauto?: boolean | undefined; - cmax?: number | undefined; - cmin?: number | undefined; - autocolorscale?: boolean | undefined; - reversescale?: boolean | undefined; - opacity: number | number[]; - size: number | number[]; - maxdisplayed?: number | undefined; - sizeref?: number | undefined; - sizemax?: number | undefined; - sizemin?: number | undefined; - sizemode?: "diameter" | "area" | undefined; - showscale?: boolean | undefined; - line: Partial; - pad?: Partial | undefined; - width?: number | undefined; - colorbar?: Partial | undefined; - gradient?: - | { - type: "radial" | "horizontal" | "vertical" | "none"; - color: Color; - typesrc: any; - colorsrc: any; - } - | undefined; - pattern?: Partial; + symbol: MarkerSymbol; + color?: Color | Color[] | undefined; + colors?: Color[] | undefined; + colorscale?: ColorScale | undefined; + cauto?: boolean | undefined; + cmax?: number | undefined; + cmin?: number | undefined; + autocolorscale?: boolean | undefined; + reversescale?: boolean | undefined; + opacity: number | number[]; + size: number | number[]; + maxdisplayed?: number | undefined; + sizeref?: number | undefined; + sizemax?: number | undefined; + sizemin?: number | undefined; + sizemode?: 'diameter' | 'area' | undefined; + showscale?: boolean | undefined; + line: Partial; + pad?: Partial | undefined; + width?: number | undefined; + colorbar?: Partial | undefined; + gradient?: + | { + type: 'radial' | 'horizontal' | 'vertical' | 'none'; + color: Color; + typesrc: any; + colorsrc: any; + } + | undefined; + pattern?: Partial; } export type ScatterMarker = PlotMarker; export interface ScatterMarkerLine { - width: number | number[]; - color: Color; - cauto?: boolean | undefined; - cmax?: number | undefined; - cmin?: number | undefined; - cmid?: number | undefined; - colorscale?: ColorScale | undefined; - autocolorscale?: boolean | undefined; - reversescale?: boolean | undefined; - coloraxis?: string | undefined; + width: number | number[]; + color: Color; + cauto?: boolean | undefined; + cmax?: number | undefined; + cmin?: number | undefined; + cmid?: number | undefined; + colorscale?: ColorScale | undefined; + autocolorscale?: boolean | undefined; + reversescale?: boolean | undefined; + coloraxis?: string | undefined; } export interface ScatterLine { - color: Color; - width: number; - dash: Dash; - shape: "linear" | "spline" | "hv" | "vh" | "hvh" | "vhv"; - smoothing: number; - simplify: boolean; + color: Color; + width: number; + dash: Dash; + shape: 'linear' | 'spline' | 'hv' | 'vh' | 'hvh' | 'vhv'; + smoothing: number; + simplify: boolean; } export interface Font { - color: Color; - /** - * HTML font family - the typeface that will be applied by the web browser. - * The web browser will only be able to apply a font if it is available on the system - * which it operates. Provide multiple font families, separated by commas, to indicate - * the preference in which to apply fonts if they aren't available on the system. - * The plotly service (at https://plot.ly or on-premise) generates images on a server, - * where only a select number of fonts are installed and supported. - * These include *Arial*, *Balto*, *Courier New*, *Droid Sans*, *Droid Serif*, - * *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, - * *PT Sans Narrow*, *Raleway*, *Times New Roman*. - * @default "Arial, sans-serif" - */ - family: string; - /** - * Sets the shape and color of the shadow behind text. "auto" places minimal shadow and applies contrast text font color. See https://developer.mozilla.org/en-US/docs/Web/CSS/text-shadow for additional options. - * @default "none" - */ - shadow: string; - /** - * number greater than or equal to 1 - * @default 13 - */ - size: number; - /** - * Sets the weight (or boldness) of the font. - * number between or equal to 1 and 1000 - * @default normal - */ - weight: number; + color: Color; + /** + * HTML font family - the typeface that will be applied by the web browser. + * The web browser will only be able to apply a font if it is available on the system + * which it operates. Provide multiple font families, separated by commas, to indicate + * the preference in which to apply fonts if they aren't available on the system. + * The plotly service (at https://plot.ly or on-premise) generates images on a server, + * where only a select number of fonts are installed and supported. + * These include *Arial*, *Balto*, *Courier New*, *Droid Sans*, *Droid Serif*, + * *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, + * *PT Sans Narrow*, *Raleway*, *Times New Roman*. + * @default "Arial, sans-serif" + */ + family: string; + /** + * Sets the shape and color of the shadow behind text. "auto" places minimal shadow and applies contrast text font color. See https://developer.mozilla.org/en-US/docs/Web/CSS/text-shadow for additional options. + * @default "none" + */ + shadow: string; + /** + * number greater than or equal to 1 + * @default 13 + */ + size: number; + /** + * Sets the weight (or boldness) of the font. + * number between or equal to 1 and 1000 + * @default normal + */ + weight: number; } export interface Config { - /** - * Determines whether math should be typeset or not, - * when MathJax (either v2 or v3) is present on the page. - */ - typesetMath: boolean; - - /** DO autosize once regardless of layout.autosize (use default width or height values otherwise) */ - autosizable: boolean; - - /** set the length of the undo/redo queue */ - queueLength: number; - - /** if we DO autosize, do we fill the container or the screen? */ - fillFrame: boolean; - - /** if we DO autosize, set the frame margins in percents of plot size */ - frameMargins: number; - - /** Set global transform to be applied to all traces with no specification needed */ - globalTransforms: any[]; - - /** Which localization should we use? Should be a string like 'en' or 'en-US' */ - locale: string; - - /** - * Localization definitions - * Locales can be provided either here (specific to one chart) or globally - * by registering them as modules. - * Should be an object of objects {locale: {dictionary: {...}, format: {...}}} - * { - * da: { - * dictionary: {'Reset axes': 'Nulstil aksler', ...}, - * format: {months: [...], shortMonths: [...]} - * }, - * ... - * } - * All parts are optional. When looking for translation or format fields, we - * look first for an exact match in a config locale, then in a registered - * module. If those fail, we strip off any regionalization ('en-US' -> 'en') - * and try each (config, registry) again. The final fallback for translation - * is untranslated (which is US English) and for formats is the base English - * (the only consequence being the last fallback date format %x is DD/MM/YYYY - * instead of MM/DD/YYYY). Currently `grouping` and `currency` are ignored - * for our automatic number formatting, but can be used in custom formats. - */ - locales: {}; - - /** Make the chart responsive to window size */ - responsive: boolean; + /** + * Determines whether math should be typeset or not, + * when MathJax (either v2 or v3) is present on the page. + */ + typesetMath: boolean; + + /** DO autosize once regardless of layout.autosize (use default width or height values otherwise) */ + autosizable: boolean; + + /** set the length of the undo/redo queue */ + queueLength: number; + + /** if we DO autosize, do we fill the container or the screen? */ + fillFrame: boolean; + + /** if we DO autosize, set the frame margins in percents of plot size */ + frameMargins: number; + + /** Set global transform to be applied to all traces with no specification needed */ + globalTransforms: any[]; + + /** Which localization should we use? Should be a string like 'en' or 'en-US' */ + locale: string; + + /** + * Localization definitions + * Locales can be provided either here (specific to one chart) or globally + * by registering them as modules. + * Should be an object of objects {locale: {dictionary: {...}, format: {...}}} + * { + * da: { + * dictionary: {'Reset axes': 'Nulstil aksler', ...}, + * format: {months: [...], shortMonths: [...]} + * }, + * ... + * } + * All parts are optional. When looking for translation or format fields, we + * look first for an exact match in a config locale, then in a registered + * module. If those fail, we strip off any regionalization ('en-US' -> 'en') + * and try each (config, registry) again. The final fallback for translation + * is untranslated (which is US English) and for formats is the base English + * (the only consequence being the last fallback date format %x is DD/MM/YYYY + * instead of MM/DD/YYYY). Currently `grouping` and `currency` are ignored + * for our automatic number formatting, but can be used in custom formats. + */ + locales: {}; + + /** Make the chart responsive to window size */ + responsive: boolean; } // Components export interface RangeSlider { - visible: boolean; - thickness: number; - range: [Datum, Datum]; - borderwidth: number; - bordercolor: string; - bgcolor: string; + visible: boolean; + thickness: number; + range: [Datum, Datum]; + borderwidth: number; + bordercolor: string; + bgcolor: string; } export interface RangeSelectorButton { - step: "second" | "minute" | "hour" | "day" | "month" | "year" | "all"; - stepmode: "backward" | "todate"; - count: number; - label: string; + step: 'second' | 'minute' | 'hour' | 'day' | 'month' | 'year' | 'all'; + stepmode: 'backward' | 'todate'; + count: number; + label: string; } export interface RangeSelector extends Label { - buttons: Array>; - visible: boolean; - x: number; - xanchor: "auto" | "left" | "center" | "right"; - y: number; - yanchor: "auto" | "top" | "middle" | "bottom"; - activecolor: string; - borderwidth: number; + buttons: Array>; + visible: boolean; + x: number; + xanchor: 'auto' | 'left' | 'center' | 'right'; + y: number; + yanchor: 'auto' | 'top' | 'middle' | 'bottom'; + activecolor: string; + borderwidth: number; } export interface Label { - /** Sets the background color of all hover labels on graph. */ - bgcolor: string; + /** Sets the background color of all hover labels on graph. */ + bgcolor: string; - /** Sets the border color of all hover labels on graph. */ - bordercolor: string; + /** Sets the border color of all hover labels on graph. */ + bordercolor: string; - /** Sets the default hover label font used by all traces on the graph. */ - font: Partial; + /** Sets the default hover label font used by all traces on the graph. */ + font: Partial; } export interface LegendTitle { - font: Partial; - side: "top" | "left" | "top left" | "top center" | "top right"; - text: string; + font: Partial; + side: 'top' | 'left' | 'top left' | 'top center' | 'top right'; + text: string; } export interface HoverLabel extends Label { - /** - * Sets the horizontal alignment of the text content within hover label box. - * @default "auto" - */ - align: "left" | "right" | "auto"; + /** + * Sets the horizontal alignment of the text content within hover label box. + * @default "auto" + */ + align: 'left' | 'right' | 'auto'; - /** - * Sets the default length (in number of characters) of the trace name - * in the hover labels for all traces. - * -1 shows the whole name regardless of length. - * @default 15 - */ - namelength: number; + /** + * Sets the default length (in number of characters) of the trace name + * in the hover labels for all traces. + * -1 shows the whole name regardless of length. + * @default 15 + */ + namelength: number; } export interface Annotations extends Label { - /** Determines whether or not this annotation is visible. */ - visible: boolean; - - /** - * Sets the text associated with this annotation. - * Plotly uses a subset of HTML tags to do things like - * newline (
), bold (), italics (), - * hyperlinks (). Tags , , - * are also supported. - */ - text: string; - - /** Sets the angle at which the `text` is drawn with respect to the horizontal. */ - textangle: string; - - /** - * Sets an explicit width for the text box. null (default) lets the - * text set the box width. Wider text will be clipped. - * There is no automatic wrapping; use
to start a new line. - */ - width: number; - - /** - * Sets an explicit height for the text box. null (default) lets the - * text set the box height. Taller text will be clipped. - */ - height: number; - - /** Sets the opacity of the annotation (text + arrow). */ - opacity: number; - - /** - * Sets the horizontal alignment of the `text` within the box. - * Has an effect only if `text` spans more two or more lines - * (i.e. `text` contains one or more
HTML tags) or if an - * explicit width is set to override the text width. - */ - align: "left" | "center" | "right"; - - /** - * Sets the vertical alignment of the `text` within the box. - * Has an effect only if an explicit height is set to override the text height. - */ - valign: "top" | "middle" | "bottom"; - - /** Sets the padding (in px) between the `text` and the enclosing border. */ - borderpad: number; - - /** Sets the width (in px) of the border enclosing the annotation `text`. */ - borderwidth: number; - - /** - * Determines whether or not the annotation is drawn with an arrow. - * If *true*, `text` is placed near the arrow's tail. - * If *false*, `text` lines up with the `x` and `y` provided. - */ - showarrow: boolean; - - /** Sets the color of the annotation arrow. */ - arrowcolor: string; - - /** Sets the end annotation arrow head style. */ - arrowhead: number; - - /** Sets the start annotation arrow head style. */ - startarrowhead: number; - - /** Sets the annotation arrow head position. */ - arrowside: "end" | "start"; - - /** - * Sets the size of the end annotation arrow head, relative to `arrowwidth`. - * A value of 1 (default) gives a head about 3x as wide as the line. - */ - arrowsize: number; - - /** - * Sets the size of the start annotation arrow head, relative to `arrowwidth`. - * A value of 1 (default) gives a head about 3x as wide as the line. - */ - startarrowsize: number; - - /** Sets the width (in px) of annotation arrow line. */ - arrowwidth: number; - - /** - * Sets a distance, in pixels, to move the end arrowhead away from the - * position it is pointing at, for example to point at the edge of - * a marker independent of zoom. Note that this shortens the arrow - * from the `ax` / `ay` vector, in contrast to `xshift` / `yshift` - * which moves everything by this amount. - */ - standoff: number; - - /** - * Sets a distance, in pixels, to move the start arrowhead away from the - * position it is pointing at, for example to point at the edge of - * a marker independent of zoom. Note that this shortens the arrow - * from the `ax` / `ay` vector, in contrast to `xshift` / `yshift` - * which moves everything by this amount. - */ - startstandoff: number; - - /** - * Sets the x component of the arrow tail about the arrow head. - * If `axref` is `pixel`, a positive (negative) - * component corresponds to an arrow pointing - * from right to left (left to right). - * If `axref` is an axis, this is an absolute value on that axis, - * like `x`, NOT a relative value. - */ - ax: number; - - /** - * Sets the y component of the arrow tail about the arrow head. - * If `ayref` is `pixel`, a positive (negative) - * component corresponds to an arrow pointing - * from bottom to top (top to bottom). - * If `ayref` is an axis, this is an absolute value on that axis, - * like `y`, NOT a relative value. - */ - ay: number; - - /** - * Indicates in what terms the tail of the annotation (ax,ay) - * is specified. If `pixel`, `ax` is a relative offset in pixels - * from `x`. If set to an x axis id (e.g. *x* or *x2*), `ax` is - * specified in the same terms as that axis. This is useful - * for trendline annotations which should continue to indicate - * the correct trend when zoomed. - */ - axref: "pixel" | XAxisName; - - /** - * Indicates in what terms the tail of the annotation (ax,ay) - * is specified. If `pixel`, `ay` is a relative offset in pixels - * from `y`. If set to a y axis id (e.g. *y* or *y2*), `ay` is - * specified in the same terms as that axis. This is useful - * for trendline annotations which should continue to indicate - * the correct trend when zoomed. - */ - ayref: "pixel" | YAxisName; - - /** - * Sets the annotation's x coordinate axis. - * If set to an x axis id (e.g. *x* or *x2*), the `x` position refers to an x coordinate - * If set to *paper*, the `x` position refers to the distance from - * the left side of the plotting area in normalized coordinates - * where 0 (1) corresponds to the left (right) side. - */ - xref: "paper" | XAxisName; - - /** - * Sets the annotation's x position. - * If the axis `type` is *log*, then you must take the log of your desired range. - * If the axis `type` is *date*, it should be date strings, like date data, - * though Date objects and unix milliseconds will be accepted and converted to strings. - * If the axis `type` is *category*, it should be numbers, using the scale where each - * category is assigned a serial number from zero in the order it appears. - */ - x: number | string; - - /** - * Sets the text box's horizontal position anchor - * This anchor binds the `x` position to the *left*, *center* or *right* of the annotation. - * For example, if `x` is set to 1, `xref` to *paper* and `xanchor` to *right* then the - * right-most portion of the annotation lines up with the right-most edge of the plotting area. - * If *auto*, the anchor is equivalent to *center* for data-referenced annotations or if there - * is an arrow, whereas for paper-referenced with no arrow, the anchor picked corresponds to the closest side. - */ - xanchor: "auto" | "left" | "center" | "right"; - - /** - * Shifts the position of the whole annotation and arrow to the - * right (positive) or left (negative) by this many pixels. - */ - xshift: number; - - /** - * Sets the annotation's y coordinate axis. - * If set to an y axis id (e.g. *y* or *y2*), the `y` position refers to an y coordinate - * If set to *paper*, the `y` position refers to the distance from - * the bottom of the plotting area in normalized coordinates - * where 0 (1) corresponds to the bottom (top). - */ - yref: "paper" | YAxisName; - - /** - * Sets the annotation's y position. - * If the axis `type` is *log*, then you must take the log of your desired range. - * If the axis `type` is *date*, it should be date strings, like date data, - * though Date objects and unix milliseconds will be accepted and converted to strings. - * If the axis `type` is *category*, it should be numbers, using the scale where each - * category is assigned a serial number from zero in the order it appears. - */ - y: number | string; - - /** - * Sets the text box's vertical position anchor - * This anchor binds the `y` position to the *top*, *middle* or *bottom* of the annotation. - * For example, if `y` is set to 1, `yref` to *paper* and `yanchor` to *top* then the - * top-most portion of the annotation lines up with the top-most edge of the plotting area. - * If *auto*, the anchor is equivalent to *middle* for data-referenced annotations or if - * there is an arrow, whereas for paper-referenced with no arrow, the anchor picked - * corresponds to the closest side. - */ - yanchor: "auto" | "top" | "middle" | "bottom"; - - /** - * Shifts the position of the whole annotation and arrow up - * (positive) or down (negative) by this many pixels. - */ - yshift: number; - - /** - * Makes this annotation respond to clicks on the plot. - * If you click a data point that exactly matches the `x` and `y` values of this annotation, - * and it is hidden (visible: false), it will appear. In *onoff* mode, you must click the same - * point again to make it disappear, so if you click multiple points, you can show multiple - * annotations. In *onout* mode, a click anywhere else in the plot (on another data point or not) - * will hide this annotation. If you need to show/hide this annotation in response to different - * `x` or `y` values, you can set `xclick` and/or `yclick`. This is useful for example to label - * the side of a bar. To label markers though, `standoff` is preferred over `xclick` and `yclick`. - */ - clicktoshow: false | "onoff" | "onout"; - - /** - * Toggle this annotation when clicking a data point whose `x` value - * is `xclick` rather than the annotation's `x` value. - */ - xclick: any; - - /** - * Toggle this annotation when clicking a data point whose `y` value - * is `yclick` rather than the annotation's `y` value. - */ - yclick: any; - - /** - * Sets text to appear when hovering over this annotation. - * If omitted or blank, no hover label will appear. - */ - hovertext: string; - - hoverlabel: Partial; - - /** - * Determines whether the annotation text box captures mouse move and click events, - * or allows those events to pass through to data points in the plot that may be - * behind the annotation. By default `captureevents` is *false* unless `hovertext` - * is provided. If you use the event `plotly_clickannotation` without `hovertext` - * you must explicitly enable `captureevents`. - */ - captureevents: boolean; + /** Determines whether or not this annotation is visible. */ + visible: boolean; + + /** + * Sets the text associated with this annotation. + * Plotly uses a subset of HTML tags to do things like + * newline (
), bold (), italics (), + * hyperlinks (). Tags , , + * are also supported. + */ + text: string; + + /** Sets the angle at which the `text` is drawn with respect to the horizontal. */ + textangle: string; + + /** + * Sets an explicit width for the text box. null (default) lets the + * text set the box width. Wider text will be clipped. + * There is no automatic wrapping; use
to start a new line. + */ + width: number; + + /** + * Sets an explicit height for the text box. null (default) lets the + * text set the box height. Taller text will be clipped. + */ + height: number; + + /** Sets the opacity of the annotation (text + arrow). */ + opacity: number; + + /** + * Sets the horizontal alignment of the `text` within the box. + * Has an effect only if `text` spans more two or more lines + * (i.e. `text` contains one or more
HTML tags) or if an + * explicit width is set to override the text width. + */ + align: 'left' | 'center' | 'right'; + + /** + * Sets the vertical alignment of the `text` within the box. + * Has an effect only if an explicit height is set to override the text height. + */ + valign: 'top' | 'middle' | 'bottom'; + + /** Sets the padding (in px) between the `text` and the enclosing border. */ + borderpad: number; + + /** Sets the width (in px) of the border enclosing the annotation `text`. */ + borderwidth: number; + + /** + * Determines whether or not the annotation is drawn with an arrow. + * If *true*, `text` is placed near the arrow's tail. + * If *false*, `text` lines up with the `x` and `y` provided. + */ + showarrow: boolean; + + /** Sets the color of the annotation arrow. */ + arrowcolor: string; + + /** Sets the end annotation arrow head style. */ + arrowhead: number; + + /** Sets the start annotation arrow head style. */ + startarrowhead: number; + + /** Sets the annotation arrow head position. */ + arrowside: 'end' | 'start'; + + /** + * Sets the size of the end annotation arrow head, relative to `arrowwidth`. + * A value of 1 (default) gives a head about 3x as wide as the line. + */ + arrowsize: number; + + /** + * Sets the size of the start annotation arrow head, relative to `arrowwidth`. + * A value of 1 (default) gives a head about 3x as wide as the line. + */ + startarrowsize: number; + + /** Sets the width (in px) of annotation arrow line. */ + arrowwidth: number; + + /** + * Sets a distance, in pixels, to move the end arrowhead away from the + * position it is pointing at, for example to point at the edge of + * a marker independent of zoom. Note that this shortens the arrow + * from the `ax` / `ay` vector, in contrast to `xshift` / `yshift` + * which moves everything by this amount. + */ + standoff: number; + + /** + * Sets a distance, in pixels, to move the start arrowhead away from the + * position it is pointing at, for example to point at the edge of + * a marker independent of zoom. Note that this shortens the arrow + * from the `ax` / `ay` vector, in contrast to `xshift` / `yshift` + * which moves everything by this amount. + */ + startstandoff: number; + + /** + * Sets the x component of the arrow tail about the arrow head. + * If `axref` is `pixel`, a positive (negative) + * component corresponds to an arrow pointing + * from right to left (left to right). + * If `axref` is an axis, this is an absolute value on that axis, + * like `x`, NOT a relative value. + */ + ax: number; + + /** + * Sets the y component of the arrow tail about the arrow head. + * If `ayref` is `pixel`, a positive (negative) + * component corresponds to an arrow pointing + * from bottom to top (top to bottom). + * If `ayref` is an axis, this is an absolute value on that axis, + * like `y`, NOT a relative value. + */ + ay: number; + + /** + * Indicates in what terms the tail of the annotation (ax,ay) + * is specified. If `pixel`, `ax` is a relative offset in pixels + * from `x`. If set to an x axis id (e.g. *x* or *x2*), `ax` is + * specified in the same terms as that axis. This is useful + * for trendline annotations which should continue to indicate + * the correct trend when zoomed. + */ + axref: 'pixel' | XAxisName; + + /** + * Indicates in what terms the tail of the annotation (ax,ay) + * is specified. If `pixel`, `ay` is a relative offset in pixels + * from `y`. If set to a y axis id (e.g. *y* or *y2*), `ay` is + * specified in the same terms as that axis. This is useful + * for trendline annotations which should continue to indicate + * the correct trend when zoomed. + */ + ayref: 'pixel' | YAxisName; + + /** + * Sets the annotation's x coordinate axis. + * If set to an x axis id (e.g. *x* or *x2*), the `x` position refers to an x coordinate + * If set to *paper*, the `x` position refers to the distance from + * the left side of the plotting area in normalized coordinates + * where 0 (1) corresponds to the left (right) side. + */ + xref: 'paper' | XAxisName; + + /** + * Sets the annotation's x position. + * If the axis `type` is *log*, then you must take the log of your desired range. + * If the axis `type` is *date*, it should be date strings, like date data, + * though Date objects and unix milliseconds will be accepted and converted to strings. + * If the axis `type` is *category*, it should be numbers, using the scale where each + * category is assigned a serial number from zero in the order it appears. + */ + x: number | string; + + /** + * Sets the text box's horizontal position anchor + * This anchor binds the `x` position to the *left*, *center* or *right* of the annotation. + * For example, if `x` is set to 1, `xref` to *paper* and `xanchor` to *right* then the + * right-most portion of the annotation lines up with the right-most edge of the plotting area. + * If *auto*, the anchor is equivalent to *center* for data-referenced annotations or if there + * is an arrow, whereas for paper-referenced with no arrow, the anchor picked corresponds to the closest side. + */ + xanchor: 'auto' | 'left' | 'center' | 'right'; + + /** + * Shifts the position of the whole annotation and arrow to the + * right (positive) or left (negative) by this many pixels. + */ + xshift: number; + + /** + * Sets the annotation's y coordinate axis. + * If set to an y axis id (e.g. *y* or *y2*), the `y` position refers to an y coordinate + * If set to *paper*, the `y` position refers to the distance from + * the bottom of the plotting area in normalized coordinates + * where 0 (1) corresponds to the bottom (top). + */ + yref: 'paper' | YAxisName; + + /** + * Sets the annotation's y position. + * If the axis `type` is *log*, then you must take the log of your desired range. + * If the axis `type` is *date*, it should be date strings, like date data, + * though Date objects and unix milliseconds will be accepted and converted to strings. + * If the axis `type` is *category*, it should be numbers, using the scale where each + * category is assigned a serial number from zero in the order it appears. + */ + y: number | string; + + /** + * Sets the text box's vertical position anchor + * This anchor binds the `y` position to the *top*, *middle* or *bottom* of the annotation. + * For example, if `y` is set to 1, `yref` to *paper* and `yanchor` to *top* then the + * top-most portion of the annotation lines up with the top-most edge of the plotting area. + * If *auto*, the anchor is equivalent to *middle* for data-referenced annotations or if + * there is an arrow, whereas for paper-referenced with no arrow, the anchor picked + * corresponds to the closest side. + */ + yanchor: 'auto' | 'top' | 'middle' | 'bottom'; + + /** + * Shifts the position of the whole annotation and arrow up + * (positive) or down (negative) by this many pixels. + */ + yshift: number; + + /** + * Makes this annotation respond to clicks on the plot. + * If you click a data point that exactly matches the `x` and `y` values of this annotation, + * and it is hidden (visible: false), it will appear. In *onoff* mode, you must click the same + * point again to make it disappear, so if you click multiple points, you can show multiple + * annotations. In *onout* mode, a click anywhere else in the plot (on another data point or not) + * will hide this annotation. If you need to show/hide this annotation in response to different + * `x` or `y` values, you can set `xclick` and/or `yclick`. This is useful for example to label + * the side of a bar. To label markers though, `standoff` is preferred over `xclick` and `yclick`. + */ + clicktoshow: false | 'onoff' | 'onout'; + + /** + * Toggle this annotation when clicking a data point whose `x` value + * is `xclick` rather than the annotation's `x` value. + */ + xclick: any; + + /** + * Toggle this annotation when clicking a data point whose `y` value + * is `yclick` rather than the annotation's `y` value. + */ + yclick: any; + + /** + * Sets text to appear when hovering over this annotation. + * If omitted or blank, no hover label will appear. + */ + hovertext: string; + + hoverlabel: Partial; + + /** + * Determines whether the annotation text box captures mouse move and click events, + * or allows those events to pass through to data points in the plot that may be + * behind the annotation. By default `captureevents` is *false* unless `hovertext` + * is provided. If you use the event `plotly_clickannotation` without `hovertext` + * you must explicitly enable `captureevents`. + */ + captureevents: boolean; } export interface Domain { - x: number[]; - y: number[]; - row: number; - column: number; + x: number[]; + y: number[]; + row: number; + column: number; } export interface Padding { - /** - * The amount of padding (in px) along the top of the component. - */ - t: number; - /** - * The amount of padding (in px) on the right side of the component. - */ - r: number; - /** - * The amount of padding (in px) along the bottom of the component. - */ - b: number; - /** - * The amount of padding (in px) on the left side of the component. - */ - l: number; - editType: "arraydraw"; + /** + * The amount of padding (in px) along the top of the component. + */ + t: number; + /** + * The amount of padding (in px) on the right side of the component. + */ + r: number; + /** + * The amount of padding (in px) along the bottom of the component. + */ + b: number; + /** + * The amount of padding (in px) on the left side of the component. + */ + l: number; + editType: 'arraydraw'; } /** * 'Sets the pattern within the marker. */ export interface Pattern { - /** - * Sets the shape of the pattern fill. - * By default, no pattern is used for filling the area. - */ - shape?: "" | "/" | "\\" | "x" | "-" | "|" | "+" | "."; - /** - * Determines whether `marker.color` should be used - * as a default to `bgcolor` or a `fgcolor`. - */ - fillmode?: "replace" | "overlay"; - /** - * When there is no colorscale sets the color of background pattern fill. - * Defaults to a `marker.color` background when `fillmode` is *overlay*. - * Otherwise, defaults to a transparent background. - */ - bgcolor?: string; - /** - * When there is no colorscale sets the color of foreground pattern fill. - * Defaults to a `marker.color` background when `fillmode` is *replace*. - * Otherwise, defaults to dark grey or white - * to increase contrast with the `bgcolor`. - */ - fgcolor?: string; - /** - * Sets the opacity of the foreground pattern fill. - * Defaults to a 0.5 when `fillmode` is *overlay*. - * Otherwise, defaults to 1. - */ - fgopacity?: string; - /** - * Sets the size of unit squares of the pattern fill in pixels, - * which corresponds to the interval of repetition of the pattern. - */ - size?: number; - /** - * Sets the solidity of the pattern fill. - * Solidity is roughly the fraction of the area filled by the pattern. - * Solidity of 0 shows only the background color without pattern - * and solidty of 1 shows only the foreground color without pattern. - */ - solidity?: number; + /** + * Sets the shape of the pattern fill. + * By default, no pattern is used for filling the area. + */ + shape?: '' | '/' | '\\' | 'x' | '-' | '|' | '+' | '.'; + /** + * Determines whether `marker.color` should be used + * as a default to `bgcolor` or a `fgcolor`. + */ + fillmode?: 'replace' | 'overlay'; + /** + * When there is no colorscale sets the color of background pattern fill. + * Defaults to a `marker.color` background when `fillmode` is *overlay*. + * Otherwise, defaults to a transparent background. + */ + bgcolor?: string; + /** + * When there is no colorscale sets the color of foreground pattern fill. + * Defaults to a `marker.color` background when `fillmode` is *replace*. + * Otherwise, defaults to dark grey or white + * to increase contrast with the `bgcolor`. + */ + fgcolor?: string; + /** + * Sets the opacity of the foreground pattern fill. + * Defaults to a 0.5 when `fillmode` is *overlay*. + * Otherwise, defaults to 1. + */ + fgopacity?: string; + /** + * Sets the size of unit squares of the pattern fill in pixels, + * which corresponds to the interval of repetition of the pattern. + */ + size?: number; + /** + * Sets the solidity of the pattern fill. + * Solidity is roughly the fraction of the area filled by the pattern. + * Solidity of 0 shows only the background color without pattern + * and solidty of 1 shows only the foreground color without pattern. + */ + solidity?: number; } diff --git a/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts b/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts index 8574f88dfbf5c..a32e2d866a8ed 100644 --- a/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts +++ b/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts @@ -1,6 +1,7 @@ /* eslint-disable one-var */ /* eslint-disable vars-on-top */ /* eslint-disable no-var */ +/* eslint-disable @typescript-eslint/no-explicit-any */ import * as React from 'react'; import { bin as d3Bin, extent as d3Extent, sum as d3Sum, min as d3Min, max as d3Max, merge as d3Merge } from 'd3-array'; import { scaleLinear as d3ScaleLinear } from 'd3-scale'; @@ -24,12 +25,12 @@ import { IHorizontalBarChartWithAxisProps } from '../HorizontalBarChartWithAxis/ import { ILineChartProps } from '../LineChart/index'; import { IAreaChartProps } from '../AreaChart/index'; import { IHeatMapChartProps } from '../HeatMapChart/index'; -import { /* DataVizPalette, */getNextColor } from '../../utilities/colors'; +import { /* DataVizPalette, */ getNextColor } from '../../utilities/colors'; import { GaugeChartVariant, IGaugeChartProps, IGaugeChartSegment } from '../GaugeChart/index'; import { IGroupedVerticalBarChartProps } from '../GroupedVerticalBarChart/index'; import { IVerticalBarChartProps } from '../VerticalBarChart/index'; import { Layout, PlotlySchema, PieData, PlotData, SankeyData } from './PlotlySchema'; -import type { Datum, TypedArray } from './PlotlySchema'; +import type { Datum, TypedArray } from './PlotlySchema'; const isDate = (value: any): boolean => !isNaN(Date.parse(value)); const isNumber = (value: any): boolean => !isNaN(parseFloat(value)) && isFinite(value); @@ -44,44 +45,46 @@ const isMonth = (possiblyMonthValue: any, presentYear: number): boolean => { export const isDateArray = (data: Datum[] | Datum[][] | TypedArray): boolean => { if (!isArrayOrTypedArray(data)) { - return false; + return false; } if (Array.isArray(data[0])) { - // Handle 2D array - return (data as Datum[][]).every(innerArray => innerArray.every(isDate)); + // Handle 2D array + return (data as Datum[][]).every(innerArray => innerArray.every(isDate)); } else { - // Handle 1D array - return (data as Datum[]).every(isDate); + // Handle 1D array + return (data as Datum[]).every(isDate); } }; export const isNumberArray = (data: Datum[] | Datum[][] | TypedArray): boolean => { if (!isArrayOrTypedArray(data)) { - return false; + return false; } if (Array.isArray(data[0])) { - // Handle 2D array - return (data as Datum[][]).every(innerArray => innerArray.every(isNumber)); + // Handle 2D array + return (data as Datum[][]).every(innerArray => innerArray.every(isNumber)); } else { - // Handle 1D array - return (data as Datum[]).every(isNumber); + // Handle 1D array + return (data as Datum[]).every(isNumber); } }; export const isMonthArray = (data: Datum[] | Datum[][] | TypedArray): boolean => { if (!isArrayOrTypedArray(data)) { return false; -} + } if (data.length > 0) { const presentYear = new Date().getFullYear(); if (Array.isArray(data[0])) { // Handle 2D array - return (data as Datum[][]).every(innerArray => innerArray.every(possiblyMonthValue => isMonth(possiblyMonthValue, presentYear))); + return (data as Datum[][]).every(innerArray => + innerArray.every(possiblyMonthValue => isMonth(possiblyMonthValue, presentYear)), + ); } else { - // Handle 1D array - return (data as Datum[]).every(possiblyMonthValue => isMonth(possiblyMonthValue, presentYear)); + // Handle 1D array + return (data as Datum[]).every(possiblyMonthValue => isMonth(possiblyMonthValue, presentYear)); } } return false; @@ -89,16 +92,9 @@ export const isMonthArray = (data: Datum[] | Datum[][] | TypedArray): boolean => function getTitles(layout: Partial | undefined) { const titles = { - chartTitle: - typeof layout?.title === 'string' ? layout.title : layout?.title?.text?? '', - xAxisTitle: - typeof layout?.xaxis?.title === 'string' - ? layout?.xaxis?.title - : layout?.xaxis?.title?.text?? '', - yAxisTitle: - typeof layout?.yaxis?.title === 'string' - ? layout?.yaxis?.title - : layout?.yaxis?.title?.text?? '', + chartTitle: typeof layout?.title === 'string' ? layout.title : layout?.title?.text ?? '', + xAxisTitle: typeof layout?.xaxis?.title === 'string' ? layout?.xaxis?.title : layout?.xaxis?.title?.text ?? '', + yAxisTitle: typeof layout?.yaxis?.title === 'string' ? layout?.yaxis?.title : layout?.yaxis?.title?.text ?? '', }; return titles; } @@ -158,8 +154,8 @@ export const transformPlotlyJsonToDonutProps = ( }; }); - const width: number = input.layout?.width?? 440; - const height: number = input.layout?.height?? 220; + const width: number = input.layout?.width ?? 440; + const height: number = input.layout?.height ?? 220; const hideLabels: boolean = firstData.textinfo ? !['value', 'percent', 'label+percent'].includes(firstData.textinfo) : false; @@ -218,7 +214,7 @@ export const transformPlotlyJsonToVSBCProps = ( mapXToDataPoints[x] = { xAxisPoint: x, chartData: [], lineData: [] }; } const legend: string = series.name || `Series ${index1 + 1}`; - const yVal: number = series.y?.[index2] as number?? 0 + const yVal: number = (series.y?.[index2] as number) ?? 0; if (series.type === 'bar' || series.type === 'scatter') { const color = getColor(legend, colorMap, isDarkTheme); mapXToDataPoints[x].chartData.push({ @@ -281,7 +277,7 @@ export const transformPlotlyJsonToGVBCProps = ( mapXToDataPoints[x].series.push({ key: legend, - data: (series.y?.[index2] as number)?? 0, + data: (series.y?.[index2] as number) ?? 0, xAxisCalloutData: x as string, color, legend, @@ -404,7 +400,6 @@ export const transformPlotlyJsonToScatterChartProps = ( colorMap: React.MutableRefObject>, isDarkTheme?: boolean, ): ILineChartProps | IAreaChartProps => { - const chartData: ILineChartPoints[] = input.data.map((series: PlotData, index: number) => { if (series.x?.length > 0 && Array.isArray(series.x[0])) { throw new Error('transform to Scatter:: 2D x array not supported'); @@ -459,7 +454,6 @@ export const transformPlotlyJsonToHorizontalBarWithAxisProps = ( colorMap: React.MutableRefObject>, isDarkTheme?: boolean, ): IHorizontalBarChartWithAxisProps => { - const chartData: IHorizontalBarChartWithAxisDataPoint[] = input.data .map((series: PlotData, index: number) => { if (series.x?.length > 0 && Array.isArray(series.x[0])) { @@ -480,9 +474,9 @@ export const transformPlotlyJsonToHorizontalBarWithAxisProps = ( }) .flat(); - const chartHeight: number = input.layout?.height?? 450; - const margin: number = input.layout?.margin?.l?? 0; - const padding: number = input.layout?.margin?.pad?? 0; + const chartHeight: number = input.layout?.height ?? 450; + const margin: number = input.layout?.margin?.l ?? 0; + const padding: number = input.layout?.margin?.pad ?? 0; const availableHeight: number = chartHeight - margin - padding; const numberOfBars = (input.data[0] as PlotData).y.length; const scalingFactor = 0.01; @@ -497,7 +491,9 @@ export const transformPlotlyJsonToHorizontalBarWithAxisProps = ( xAxisTitle, yAxisTitle, secondaryYAxistitle: - typeof input.layout?.yaxis2?.title === 'string' ? input.layout?.yaxis2?.title : input.layout?.yaxis2?.title?.text || '', + typeof input.layout?.yaxis2?.title === 'string' + ? input.layout?.yaxis2?.title + : input.layout?.yaxis2?.title?.text || '', barHeight, showYAxisLables: true, styles: { @@ -520,8 +516,8 @@ export const transformPlotlyJsonToHeatmapProps = (input: PlotlySchema): IHeatMap const zVal = (firstData.z as number[][])?.[yIdx]?.[xIdx]; heatmapDataPoints.push({ - x: input.layout?.xaxis?.type === 'date' ? xVal as Date : xVal?? 0, - y: input.layout?.yaxis?.type === 'date' ? yVal as Date : yVal, + x: input.layout?.xaxis?.type === 'date' ? (xVal as Date) : xVal ?? 0, + y: input.layout?.yaxis?.type === 'date' ? (yVal as Date) : yVal, value: zVal, rectText: zVal, }); @@ -538,9 +534,11 @@ export const transformPlotlyJsonToHeatmapProps = (input: PlotlySchema): IHeatMap // Convert normalized values to actual values const domainValuesForColorScale: number[] = firstData.colorscale - ? (firstData.colorscale as Array<[number, string]>).map((arr) => arr[0] * (zMax - zMin) + zMin) + ? (firstData.colorscale as Array<[number, string]>).map(arr => arr[0] * (zMax - zMin) + zMin) + : []; + const rangeValuesForColorScale: string[] = firstData.colorscale + ? (firstData.colorscale as Array<[number, string]>).map(arr => arr[1]) : []; - const rangeValuesForColorScale: string[] = firstData.colorscale ? (firstData.colorscale as Array<[number, string]>).map((arr) => arr[1]) : []; const { chartTitle, xAxisTitle, yAxisTitle } = getTitles(input.layout); return { @@ -562,15 +560,17 @@ export const transformPlotlyJsonToSankeyProps = ( isDarkTheme?: boolean, ): ISankeyChartProps => { const { link, node } = input.data[0] as SankeyData; - const validLinks = link?.value?? [] - .map((val: number, index: number) => ({ - value: val, - source: link?.source![index], - target: link?.target![index], - })) - // eslint-disable-next-line @typescript-eslint/no-shadow - // Filter out negative nodes, unequal nodes and self-references (circular links) - .filter(x => x.source >= 0 && x.target >= 0 && x.source !== x.target); + const validLinks = + link?.value ?? + [] + .map((val: number, index: number) => ({ + value: val, + source: link?.source![index], + target: link?.target![index], + })) + // eslint-disable-next-line @typescript-eslint/no-shadow + // Filter out negative nodes, unequal nodes and self-references (circular links) + .filter(x => x.source >= 0 && x.target >= 0 && x.source !== x.target); const sankeyChartData = { nodes: node.label?.map((label: string, index: number) => { @@ -589,8 +589,8 @@ export const transformPlotlyJsonToSankeyProps = ( }), } as ISankeyChartData; - const width: number = input.layout?.width?? 440; - const height: number = input.layout?.height?? 220; + const width: number = input.layout?.width ?? 440; + const height: number = input.layout?.height ?? 220; const styles: ISankeyChartProps['styles'] = { root: { ...(typeof input.layout?.font?.size === 'number' ? { fontSize: input.layout.font?.size } : {}), @@ -631,7 +631,7 @@ export const transformPlotlyJsonToGaugeProps = ( }; }) : [ -/* { //ToDo: fix this + /* { //ToDo: fix this legend: 'Current', size: firstData.value ?? 0 - (firstData.gauge?.range?.[0] ?? 0), color: getColor('Current', colorMap, isDarkTheme), @@ -668,15 +668,15 @@ export const transformPlotlyJsonToGaugeProps = ( return { segments, - chartValue: firstData.value?? 0, + chartValue: firstData.value ?? 0, chartTitle, sublabel, // range values can be null minValue: typeof firstData.gauge?.axis?.range?.[0] === 'number' ? firstData.gauge?.axis?.range?.[0] : undefined, maxValue: typeof firstData.gauge?.axis?.range?.[1] === 'number' ? firstData.gauge?.axis?.range?.[1] : undefined, //chartValueFormat: () => firstData.value, ToDo: fix this - width: input.layout?.width?? 440, - height: input.layout?.height?? 220, + width: input.layout?.width ?? 440, + height: input.layout?.height ?? 220, styles, variant: firstData.gauge?.steps?.length ? GaugeChartVariant.MultipleSegments : GaugeChartVariant.SingleSegment, }; From ddaa435dc710f0a5c32342dbe09739e9afa1b02b Mon Sep 17 00:00:00 2001 From: "Atishay Jain (atisjai)" <98592573+AtishayMsft@users.noreply.github.com> Date: Mon, 13 Jan 2025 10:08:04 +0000 Subject: [PATCH 05/20] Update snapshot --- .../DeclarativeChartRTL.test.tsx.snap | 800 +++++++++--------- 1 file changed, 400 insertions(+), 400 deletions(-) diff --git a/packages/charts/react-charting/src/components/DeclarativeChart/__snapshots__/DeclarativeChartRTL.test.tsx.snap b/packages/charts/react-charting/src/components/DeclarativeChart/__snapshots__/DeclarativeChartRTL.test.tsx.snap index 2518fa0e844b7..8b8aba9f8b252 100644 --- a/packages/charts/react-charting/src/components/DeclarativeChart/__snapshots__/DeclarativeChartRTL.test.tsx.snap +++ b/packages/charts/react-charting/src/components/DeclarativeChart/__snapshots__/DeclarativeChartRTL.test.tsx.snap @@ -5659,7 +5659,7 @@ exports[`DeclarativeChart Should render heatmapchart in DeclarativeChart 1`] = ` Date: Mon, 13 Jan 2025 11:00:07 +0000 Subject: [PATCH 06/20] Add change file --- ...eact-charting-a8a1b0ef-ed4a-471e-bc5c-c9da614fc26f.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/@fluentui-react-charting-a8a1b0ef-ed4a-471e-bc5c-c9da614fc26f.json diff --git a/change/@fluentui-react-charting-a8a1b0ef-ed4a-471e-bc5c-c9da614fc26f.json b/change/@fluentui-react-charting-a8a1b0ef-ed4a-471e-bc5c-c9da614fc26f.json new file mode 100644 index 0000000000000..fef536a369732 --- /dev/null +++ b/change/@fluentui-react-charting-a8a1b0ef-ed4a-471e-bc5c-c9da614fc26f.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Use strongly typed interfaces for plotly schema", + "packageName": "@fluentui/react-charting", + "email": "98592573+AtishayMsft@users.noreply.github.com", + "dependentChangeType": "patch" +} From 6b9432ee9570cfe0f1f1eecbfd64f3cef2c07c1b Mon Sep 17 00:00:00 2001 From: Anush Gupta <74965306+Anush2303@users.noreply.github.com> Date: Mon, 13 Jan 2025 20:44:18 +0530 Subject: [PATCH 07/20] Sankey and gauge chart fix (#33628) --- ...-be791d81-9cfb-4576-961c-03c4f6eb85c0.json | 7 +++++ .../DeclarativeChart/PlotlySchemaAdapter.ts | 30 +++++++++---------- 2 files changed, 21 insertions(+), 16 deletions(-) create mode 100644 change/@fluentui-react-charting-be791d81-9cfb-4576-961c-03c4f6eb85c0.json diff --git a/change/@fluentui-react-charting-be791d81-9cfb-4576-961c-03c4f6eb85c0.json b/change/@fluentui-react-charting-be791d81-9cfb-4576-961c-03c4f6eb85c0.json new file mode 100644 index 0000000000000..b3c031f141fdb --- /dev/null +++ b/change/@fluentui-react-charting-be791d81-9cfb-4576-961c-03c4f6eb85c0.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "StronglyType plotly schema bug fix", + "packageName": "@fluentui/react-charting", + "email": "74965306+Anush2303@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts b/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts index a32e2d866a8ed..f97d19cee2349 100644 --- a/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts +++ b/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts @@ -25,7 +25,7 @@ import { IHorizontalBarChartWithAxisProps } from '../HorizontalBarChartWithAxis/ import { ILineChartProps } from '../LineChart/index'; import { IAreaChartProps } from '../AreaChart/index'; import { IHeatMapChartProps } from '../HeatMapChart/index'; -import { /* DataVizPalette, */ getNextColor } from '../../utilities/colors'; +import { DataVizPalette, getNextColor } from '../../utilities/colors'; import { GaugeChartVariant, IGaugeChartProps, IGaugeChartSegment } from '../GaugeChart/index'; import { IGroupedVerticalBarChartProps } from '../GroupedVerticalBarChart/index'; import { IVerticalBarChartProps } from '../VerticalBarChart/index'; @@ -560,17 +560,15 @@ export const transformPlotlyJsonToSankeyProps = ( isDarkTheme?: boolean, ): ISankeyChartProps => { const { link, node } = input.data[0] as SankeyData; - const validLinks = - link?.value ?? - [] - .map((val: number, index: number) => ({ - value: val, - source: link?.source![index], - target: link?.target![index], - })) - // eslint-disable-next-line @typescript-eslint/no-shadow - // Filter out negative nodes, unequal nodes and self-references (circular links) - .filter(x => x.source >= 0 && x.target >= 0 && x.source !== x.target); + const validLinks = (link?.value ?? []) + .map((val: number, index: number) => ({ + value: val, + source: link?.source![index], + target: link?.target![index], + })) + // eslint-disable-next-line @typescript-eslint/no-shadow + // Filter out negative nodes, unequal nodes and self-references (circular links) + .filter(x => x.source >= 0 && x.target >= 0 && x.source !== x.target); const sankeyChartData = { nodes: node.label?.map((label: string, index: number) => { @@ -631,16 +629,16 @@ export const transformPlotlyJsonToGaugeProps = ( }; }) : [ - /* { //ToDo: fix this + { legend: 'Current', - size: firstData.value ?? 0 - (firstData.gauge?.range?.[0] ?? 0), + size: firstData.value ?? 0 - (firstData.gauge?.axis?.range?.[0] ?? 0), color: getColor('Current', colorMap, isDarkTheme), }, { legend: 'Target', - size: (firstData.gauge?.range?.[1] ?? 100) - (firstData.value ?? 0), + size: (firstData.gauge?.axis?.range?.[1] ?? 100) - (firstData.value ?? 0), color: DataVizPalette.disabled, - }, */ + }, ]; let sublabel: string | undefined; From 0e0d631f4d06afdb31ffb8b3aeaf6651603ca636 Mon Sep 17 00:00:00 2001 From: "Atishay Jain (atisjai)" <98592573+AtishayMsft@users.noreply.github.com> Date: Wed, 15 Jan 2025 03:04:25 +0000 Subject: [PATCH 08/20] Refactor code --- .../DeclarativeChart/DeclarativeChart.tsx | 2 +- .../DeclarativeChart/PlotlySchemaAdapter.ts | 99 ++++++++----------- 2 files changed, 40 insertions(+), 61 deletions(-) diff --git a/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx b/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx index 829e8970c639d..3b63572e2a364 100644 --- a/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx +++ b/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx @@ -290,7 +290,7 @@ export const DeclarativeChart: React.FunctionComponent = /> ); default: - throw new Error('Unsupported chart schema'); + throw new Error(`Unsupported chart type :${plotlyInput.data[0].type}`); } }); DeclarativeChart.displayName = 'DeclarativeChart'; diff --git a/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts b/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts index f97d19cee2349..8981faca68d0c 100644 --- a/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts +++ b/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts @@ -43,51 +43,48 @@ const isMonth = (possiblyMonthValue: any, presentYear: number): boolean => { return isDate(`${possiblyMonthValue} 01, ${presentYear}`); }; -export const isDateArray = (data: Datum[] | Datum[][] | TypedArray): boolean => { +const isArrayOfType = ( + data: Datum[] | Datum[][] | TypedArray, + typeCheck: (datum: any, ...args: any[]) => boolean, + ...args: any[] +): boolean => { if (!isArrayOrTypedArray(data)) { return false; } - if (Array.isArray(data[0])) { - // Handle 2D array - return (data as Datum[][]).every(innerArray => innerArray.every(isDate)); - } else { - // Handle 1D array - return (data as Datum[]).every(isDate); - } -}; - -export const isNumberArray = (data: Datum[] | Datum[][] | TypedArray): boolean => { - if (!isArrayOrTypedArray(data)) { + if (data.length === 0) { return false; } if (Array.isArray(data[0])) { // Handle 2D array - return (data as Datum[][]).every(innerArray => innerArray.every(isNumber)); + return (data as Datum[][]).every(innerArray => innerArray.every(datum => typeCheck(datum, ...args))); } else { // Handle 1D array - return (data as Datum[]).every(isNumber); + return (data as Datum[]).every(datum => typeCheck(datum, ...args)); } }; +export const isDateArray = (data: Datum[] | Datum[][] | TypedArray): boolean => { + return isArrayOfType(data, isDate); +}; + +export const isNumberArray = (data: Datum[] | Datum[][] | TypedArray): boolean => { + return isArrayOfType(data, isNumber); +}; + export const isMonthArray = (data: Datum[] | Datum[][] | TypedArray): boolean => { - if (!isArrayOrTypedArray(data)) { - return false; + const presentYear = new Date().getFullYear(); + return isArrayOfType(data, isMonth, presentYear); +}; + +const invalidate2Dseries = (series: PlotData, chartType: string): void => { + if (series.x?.length > 0 && Array.isArray(series.x[0])) { + throw new Error(`transform to ${chartType}:: 2D x array not supported`); } - if (data.length > 0) { - const presentYear = new Date().getFullYear(); - if (Array.isArray(data[0])) { - // Handle 2D array - return (data as Datum[][]).every(innerArray => - innerArray.every(possiblyMonthValue => isMonth(possiblyMonthValue, presentYear)), - ); - } else { - // Handle 1D array - return (data as Datum[]).every(possiblyMonthValue => isMonth(possiblyMonthValue, presentYear)); - } + if (series.y?.length > 0 && Array.isArray(series.y[0])) { + throw new Error(`transform to ${chartType}:: 2D y array not supported`); } - return false; }; function getTitles(layout: Partial | undefined) { @@ -199,14 +196,10 @@ export const transformPlotlyJsonToVSBCProps = ( let yMaxValue = 0; input.data.forEach((series: PlotData, index1: number) => { - if (series.x?.length > 0 && Array.isArray(series.x[0])) { - throw new Error('transform to VSBC:: 2D x array not supported'); - } - if (series.y?.length > 0 && Array.isArray(series.y[0])) { - throw new Error('transform to VSBC:: 2D y array not supported'); - } + invalidate2Dseries(series, 'VSBC'); + if (!isNumberArray(series.y)) { - throw new Error('transform to VSBC:: y values are not numbers'); + throw new Error('transform to VSBC:: y values should be numeric'); } (series.x as Datum[])?.forEach((x: string | number, index2: number) => { @@ -258,15 +251,12 @@ export const transformPlotlyJsonToGVBCProps = ( const mapXToDataPoints: Record = {}; input.data.forEach((series: PlotData, index1: number) => { - if (series.x?.length > 0 && Array.isArray(series.x[0])) { - throw new Error('transform to GVBC:: 2D x array not supported'); - } - if (series.y?.length > 0 && Array.isArray(series.y[0])) { - throw new Error('transform to GVBC:: 2D y array not supported'); - } + invalidate2Dseries(series, 'GVBC'); + if (!isNumberArray(series.y)) { - throw new Error('transform to GVBC:: y values are not numbers'); + throw new Error('transform to GVBC:: y values should be numeric'); } + (series.x as Datum[])?.forEach((x: string | number, index2: number) => { if (!mapXToDataPoints[x]) { mapXToDataPoints[x] = { name: x.toString(), series: [] }; @@ -308,9 +298,7 @@ export const transformPlotlyJsonToVBCProps = ( const vbcData: IVerticalBarChartDataPoint[] = []; input.data.forEach((series: PlotData, index: number) => { - if (series.x?.length > 0 && Array.isArray(series.x[0])) { - throw new Error('transform to GVBC:: 2D x array not supported'); - } + invalidate2Dseries(series, 'VBC'); if (!series.x) { return; @@ -401,12 +389,7 @@ export const transformPlotlyJsonToScatterChartProps = ( isDarkTheme?: boolean, ): ILineChartProps | IAreaChartProps => { const chartData: ILineChartPoints[] = input.data.map((series: PlotData, index: number) => { - if (series.x?.length > 0 && Array.isArray(series.x[0])) { - throw new Error('transform to Scatter:: 2D x array not supported'); - } - if (series.y?.length > 0 && Array.isArray(series.y[0])) { - throw new Error('transform to Scatter:: 2D y array not supported'); - } + invalidate2Dseries(series, 'Scatter'); const xValues = series.x as Datum[]; const isString = typeof xValues[0] === 'string'; @@ -456,12 +439,8 @@ export const transformPlotlyJsonToHorizontalBarWithAxisProps = ( ): IHorizontalBarChartWithAxisProps => { const chartData: IHorizontalBarChartWithAxisDataPoint[] = input.data .map((series: PlotData, index: number) => { - if (series.x?.length > 0 && Array.isArray(series.x[0])) { - throw new Error('transform to HBC:: 2D x array not supported'); - } - if (series.y?.length > 0 && Array.isArray(series.y[0])) { - throw new Error('transform to HBC:: 2D y array not supported'); - } + invalidate2Dseries(series, 'HBC'); + return (series.y as Datum[]).map((yValue: string, i: number) => { const color = getColor(yValue, colorMap, isDarkTheme); return { @@ -499,7 +478,7 @@ export const transformPlotlyJsonToHorizontalBarWithAxisProps = ( styles: { root: { height: chartHeight, - width: typeof input.layout?.width === 'number' ? input.layout.width : 600, + width: input.layout?.width ?? 600, }, }, }; @@ -591,7 +570,7 @@ export const transformPlotlyJsonToSankeyProps = ( const height: number = input.layout?.height ?? 220; const styles: ISankeyChartProps['styles'] = { root: { - ...(typeof input.layout?.font?.size === 'number' ? { fontSize: input.layout.font?.size } : {}), + ...(input.layout?.font?.size ? { fontSize: input.layout.font?.size } : {}), }, }; const shouldResize: number = width + height; @@ -643,7 +622,7 @@ export const transformPlotlyJsonToGaugeProps = ( let sublabel: string | undefined; let sublabelColor: string | undefined; - if (typeof firstData.delta?.reference === 'number') { + if (firstData.delta?.reference) { const diff = firstData.value - firstData.delta.reference; if (diff >= 0) { sublabel = `\u25B2 ${diff}`; From 1dde2d91ac4e6ff5d9cfb62acffdae1db699ca65 Mon Sep 17 00:00:00 2001 From: "Atishay Jain (atisjai)" <98592573+AtishayMsft@users.noreply.github.com> Date: Wed, 15 Jan 2025 03:15:07 +0000 Subject: [PATCH 09/20] Refactor legend naming --- .../DeclarativeChart/PlotlySchemaAdapter.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts b/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts index 8981faca68d0c..f8089d7d768f0 100644 --- a/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts +++ b/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts @@ -87,6 +87,10 @@ const invalidate2Dseries = (series: PlotData, chartType: string): void => { } }; +const getLegend = (series: PlotData, index: number): string => { + return series.name || `Series ${index + 1}`; +}; + function getTitles(layout: Partial | undefined) { const titles = { chartTitle: typeof layout?.title === 'string' ? layout.title : layout?.title?.text ?? '', @@ -206,7 +210,7 @@ export const transformPlotlyJsonToVSBCProps = ( if (!mapXToDataPoints[x]) { mapXToDataPoints[x] = { xAxisPoint: x, chartData: [], lineData: [] }; } - const legend: string = series.name || `Series ${index1 + 1}`; + const legend: string = getLegend(series, index1); const yVal: number = (series.y?.[index2] as number) ?? 0; if (series.type === 'bar' || series.type === 'scatter') { const color = getColor(legend, colorMap, isDarkTheme); @@ -262,7 +266,7 @@ export const transformPlotlyJsonToGVBCProps = ( mapXToDataPoints[x] = { name: x.toString(), series: [] }; } if (series.type === 'bar') { - const legend: string = series.name || `Series ${index1 + 1}`; + const legend: string = getLegend(series, index1); const color = getColor(legend, colorMap, isDarkTheme); mapXToDataPoints[x].series.push({ @@ -335,7 +339,7 @@ export const transformPlotlyJsonToVBCProps = ( const totalDataPoints = d3Merge(buckets).length; buckets.forEach(bucket => { - const legend: string = series.name || `Series ${index + 1}`; + const legend: string = getLegend(series, index); const color: string = getColor(legend, colorMap, isDarkTheme); let y = bucket.length; @@ -395,7 +399,7 @@ export const transformPlotlyJsonToScatterChartProps = ( const isString = typeof xValues[0] === 'string'; const isXDate = isDateArray(xValues); const isXNumber = isNumberArray(xValues); - const legend: string = series.name || `Series ${index + 1}`; + const legend: string = getLegend(series, index); const lineColor = getColor(legend, colorMap, isDarkTheme); return { From 4ac87d0e2780f2ea6895b823b50f69f9865febd0 Mon Sep 17 00:00:00 2001 From: srmukher <120183316+srmukher@users.noreply.github.com> Date: Wed, 15 Jan 2025 17:33:03 +0530 Subject: [PATCH 10/20] Adding secondary y axis for declarative charts (#33625) --- .../DeclarativeChart/PlotlySchemaAdapter.ts | 54 ++++++++++++++++++- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts b/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts index f8089d7d768f0..1b252816c1f38 100644 --- a/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts +++ b/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts @@ -32,6 +32,11 @@ import { IVerticalBarChartProps } from '../VerticalBarChart/index'; import { Layout, PlotlySchema, PieData, PlotData, SankeyData } from './PlotlySchema'; import type { Datum, TypedArray } from './PlotlySchema'; +interface ISecondaryYAxisValues { + secondaryYAxistitle?: string; + secondaryYScaleOptions?: { yMinValue?: number; yMaxValue?: number }; +} + const isDate = (value: any): boolean => !isNaN(Date.parse(value)); const isNumber = (value: any): boolean => !isNaN(parseFloat(value)) && isFinite(value); @@ -139,6 +144,39 @@ export const getColor = ( return colorMap.current.get(legendLabel) as string; }; +const getSecondaryYAxisValues = (series: PlotData, layout: Partial | undefined) => { + const secondaryYAxisValues: ISecondaryYAxisValues = {}; + if (layout && layout.yaxis2 && series.yaxis === 'y2') { + secondaryYAxisValues.secondaryYAxistitle = + typeof layout.yaxis2.title === 'string' + ? layout.yaxis2.title + : typeof layout.yaxis2.title?.text === 'string' + ? layout.yaxis2.title.text + : ''; + if (layout.yaxis2.range) { + secondaryYAxisValues.secondaryYScaleOptions = { + yMinValue: layout.yaxis2.range[0], + yMaxValue: layout.yaxis2.range[1], + }; + } else { + const yValues = series.y as number[]; + if (yValues) { + secondaryYAxisValues.secondaryYScaleOptions = { + yMinValue: Math.min(...yValues), + yMaxValue: Math.max(...yValues), + }; + } + } + } + secondaryYAxisValues.secondaryYAxistitle = + secondaryYAxisValues.secondaryYAxistitle !== '' ? secondaryYAxisValues.secondaryYAxistitle : undefined; + secondaryYAxisValues.secondaryYScaleOptions = + secondaryYAxisValues.secondaryYScaleOptions && Object.keys(secondaryYAxisValues.secondaryYScaleOptions).length !== 0 + ? secondaryYAxisValues.secondaryYScaleOptions + : undefined; + return secondaryYAxisValues; +}; + export const transformPlotlyJsonToDonutProps = ( input: PlotlySchema, colorMap: React.MutableRefObject>, @@ -198,7 +236,7 @@ export const transformPlotlyJsonToVSBCProps = ( ): IVerticalStackedBarChartProps => { const mapXToDataPoints: { [key: string]: IVerticalStackedChartProps } = {}; let yMaxValue = 0; - + let secondaryYAxisValues: ISecondaryYAxisValues = {}; input.data.forEach((series: PlotData, index1: number) => { invalidate2Dseries(series, 'VSBC'); @@ -230,6 +268,7 @@ export const transformPlotlyJsonToVSBCProps = ( yMaxValue = Math.max(yMaxValue, yVal); }); + secondaryYAxisValues = getSecondaryYAxisValues(series, input.layout); }); const { chartTitle, xAxisTitle, yAxisTitle } = getTitles(input.layout); @@ -244,6 +283,8 @@ export const transformPlotlyJsonToVSBCProps = ( xAxisTitle, yAxisTitle, mode: 'plotly', + secondaryYAxistitle: secondaryYAxisValues.secondaryYAxistitle, + secondaryYScaleOptions: secondaryYAxisValues.secondaryYScaleOptions, }; }; @@ -253,7 +294,7 @@ export const transformPlotlyJsonToGVBCProps = ( isDarkTheme?: boolean, ): IGroupedVerticalBarChartProps => { const mapXToDataPoints: Record = {}; - + let secondaryYAxisValues: ISecondaryYAxisValues = {}; input.data.forEach((series: PlotData, index1: number) => { invalidate2Dseries(series, 'GVBC'); @@ -278,6 +319,7 @@ export const transformPlotlyJsonToGVBCProps = ( }); } }); + secondaryYAxisValues = getSecondaryYAxisValues(series, input.layout); }); const { chartTitle, xAxisTitle, yAxisTitle } = getTitles(input.layout); @@ -291,6 +333,8 @@ export const transformPlotlyJsonToGVBCProps = ( xAxisTitle, yAxisTitle, mode: 'plotly', + secondaryYAxistitle: secondaryYAxisValues.secondaryYAxistitle, + secondaryYScaleOptions: secondaryYAxisValues.secondaryYScaleOptions, }; }; @@ -392,6 +436,7 @@ export const transformPlotlyJsonToScatterChartProps = ( colorMap: React.MutableRefObject>, isDarkTheme?: boolean, ): ILineChartProps | IAreaChartProps => { + let secondaryYAxisValues: ISecondaryYAxisValues = {}; const chartData: ILineChartPoints[] = input.data.map((series: PlotData, index: number) => { invalidate2Dseries(series, 'Scatter'); @@ -401,6 +446,7 @@ export const transformPlotlyJsonToScatterChartProps = ( const isXNumber = isNumberArray(xValues); const legend: string = getLegend(series, index); const lineColor = getColor(legend, colorMap, isDarkTheme); + secondaryYAxisValues = getSecondaryYAxisValues(series, input.layout); return { legend, @@ -425,6 +471,8 @@ export const transformPlotlyJsonToScatterChartProps = ( supportNegativeData: true, xAxisTitle, yAxisTitle, + secondaryYAxistitle: secondaryYAxisValues.secondaryYAxistitle, + secondaryYScaleOptions: secondaryYAxisValues.secondaryYScaleOptions, } as IAreaChartProps; } else { return { @@ -432,6 +480,8 @@ export const transformPlotlyJsonToScatterChartProps = ( supportNegativeData: true, xAxisTitle, yAxisTitle, + secondaryYAxistitle: secondaryYAxisValues.secondaryYAxistitle, + secondaryYScaleOptions: secondaryYAxisValues.secondaryYScaleOptions, } as ILineChartProps; } }; From 54d722b751f41d1adbdf66a964ac8081d7d103ec Mon Sep 17 00:00:00 2001 From: srmukher <120183316+srmukher@users.noreply.github.com> Date: Thu, 16 Jan 2025 14:41:24 +0530 Subject: [PATCH 11/20] Adding fallback and fixes for test app crashes for 12 schema data (#33624) --- ...-17bc0f58-a206-47b6-b5dc-6bf008820978.json | 7 ++ .../DeclarativeChart/DeclarativeChart.tsx | 110 +++++++++++------- .../DeclarativeChart/PlotlySchemaAdapter.ts | 4 +- 3 files changed, 80 insertions(+), 41 deletions(-) create mode 100644 change/@fluentui-react-charting-17bc0f58-a206-47b6-b5dc-6bf008820978.json diff --git a/change/@fluentui-react-charting-17bc0f58-a206-47b6-b5dc-6bf008820978.json b/change/@fluentui-react-charting-17bc0f58-a206-47b6-b5dc-6bf008820978.json new file mode 100644 index 0000000000000..1026025406a1b --- /dev/null +++ b/change/@fluentui-react-charting-17bc0f58-a206-47b6-b5dc-6bf008820978.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Adding fallback and fixes for test app crashes for 12 schema data ", + "packageName": "@fluentui/react-charting", + "email": "120183316+srmukher@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx b/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx index 3b63572e2a364..ffbd411581a2d 100644 --- a/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx +++ b/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx @@ -122,6 +122,58 @@ export const DeclarativeChart: React.FunctionComponent = selectedLegend: activeLegends.slice(0, 1)[0], }; + const checkAndRenderChart = ( + renderChartJsx: (chartProps: ILineChartProps | IAreaChartProps) => JSX.Element, + isAreaChart: boolean = false, + ) => { + let fallbackVSBC = false; + const xValues = (plotlyInput.data[0] as PlotData).x; + const isXDate = isDateArray(xValues); + const isXNumber = isNumberArray(xValues); + const isXMonth = isMonthArray(xValues); + if (isXDate || isXNumber) { + const chartProps = { + ...transformPlotlyJsonToScatterChartProps( + { data: plotlyInput.data, layout: plotlyInput.layout }, + isAreaChart, + colorMap, + isDarkTheme, + ), + legendProps, + componentRef: chartRef, + calloutProps: { layerProps: { eventBubblingEnabled: true } }, + }; + return renderChartJsx(chartProps); + } else if (isXMonth) { + const updatedData = plotlyInput.data.map((dataPoint: PlotData) => ({ + ...dataPoint, + x: updateXValues(dataPoint.x), + })); + const chartProps = { + ...transformPlotlyJsonToScatterChartProps( + { data: updatedData, layout: plotlyInput.layout }, + isAreaChart, + colorMap, + isDarkTheme, + ), + legendProps, + componentRef: chartRef, + calloutProps: { layerProps: { eventBubblingEnabled: true } }, + }; + return renderChartJsx(chartProps); + } + // Unsupported schema, render as VerticalStackedBarChart + fallbackVSBC = true; + return ( + + ); + }; + const exportAsImage = React.useCallback( (opts?: IImageExportOptions) => { return toImage(chartRef.current?.chartContainer, { @@ -189,14 +241,10 @@ export const DeclarativeChart: React.FunctionComponent = ); } case 'scatter': - const xValues = (plotlyInput.data[0] as PlotData).x; - const isXDate = isDateArray(xValues); - const isXNumber = isNumberArray(xValues); - const isXMonth = isMonthArray(xValues); const isAreaChart = plotlyInput.data.some( (series: PlotData) => series.fill === 'tonexty' || series.fill === 'tozeroy', ); - const renderChart = (chartProps: ILineChartProps | IAreaChartProps) => { + const renderChartJsx = (chartProps: ILineChartProps | IAreaChartProps) => { if (isAreaChart) { return ( = /> ); }; - if (isXDate || isXNumber) { - const chartProps = { - ...transformPlotlyJsonToScatterChartProps(plotlyInput, isAreaChart, colorMap, isDarkTheme), - legendProps, - componentRef: chartRef, - calloutProps: { layerProps: { eventBubblingEnabled: true } }, - }; - return renderChart(chartProps); - } else if (isXMonth) { - const updatedData = plotlyInput.data.map((dataPoint: PlotData) => ({ - ...dataPoint, - x: updateXValues(dataPoint.x), - })); - const chartProps = { - ...transformPlotlyJsonToScatterChartProps( - { data: updatedData, layout: plotlyInput.layout }, - isAreaChart, - colorMap, - isDarkTheme, - ), - legendProps, - componentRef: chartRef, - calloutProps: { layerProps: { eventBubblingEnabled: true } }, - }; - return renderChart(chartProps); - } - return ( - - ); + return checkAndRenderChart(renderChartJsx, isAreaChart); case 'heatmap': return ( = /> ); default: - throw new Error(`Unsupported chart type :${plotlyInput.data[0].type}`); + const xValues = (plotlyInput.data[0] as PlotData).x; + const yValues = (plotlyInput.data[0] as PlotData).y; + if (xValues && yValues && xValues.length > 0 && yValues.length > 0) { + const renderLineChartJsx = (chartProps: ILineChartProps) => { + return ( + + ); + }; + return checkAndRenderChart(renderLineChartJsx); + } + throw new Error('Unsupported chart schema'); } }); DeclarativeChart.displayName = 'DeclarativeChart'; diff --git a/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts b/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts index 1b252816c1f38..6d87b92ebb7da 100644 --- a/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts +++ b/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts @@ -130,6 +130,7 @@ export const updateXValues = (xValues: Datum[] | Datum[][] | TypedArray): any[] }); return xValues; }; + export const getColor = ( legendLabel: string, colorMap: React.MutableRefObject>, @@ -233,6 +234,7 @@ export const transformPlotlyJsonToVSBCProps = ( input: PlotlySchema, colorMap: React.MutableRefObject>, isDarkTheme?: boolean, + fallbackVSBC?: boolean, ): IVerticalStackedBarChartProps => { const mapXToDataPoints: { [key: string]: IVerticalStackedChartProps } = {}; let yMaxValue = 0; @@ -250,7 +252,7 @@ export const transformPlotlyJsonToVSBCProps = ( } const legend: string = getLegend(series, index1); const yVal: number = (series.y?.[index2] as number) ?? 0; - if (series.type === 'bar' || series.type === 'scatter') { + if (series.type === 'bar' || series.type === 'scatter' || !!fallbackVSBC) { const color = getColor(legend, colorMap, isDarkTheme); mapXToDataPoints[x].chartData.push({ legend, From 2d1d3bcb49550036899f6849a691bd538a0e6761 Mon Sep 17 00:00:00 2001 From: "Atishay Jain (atisjai)" <98592573+AtishayMsft@users.noreply.github.com> Date: Fri, 17 Jan 2025 02:03:53 +0000 Subject: [PATCH 12/20] Refactor to remove duplicate code --- .../DeclarativeChart/DeclarativeChart.tsx | 117 ++++-------------- 1 file changed, 27 insertions(+), 90 deletions(-) diff --git a/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx b/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx index ffbd411581a2d..3436c28cafe21 100644 --- a/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx +++ b/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx @@ -116,10 +116,16 @@ export const DeclarativeChart: React.FunctionComponent = setActiveLegends(selectedLegends ?? []); }, [props.chartSchema]); - const legendProps = { - canSelectMultipleLegends: false, + const multiSelectLegendProps = { + canSelectMultipleLegends: true, onChange: onActiveLegendsChange, - selectedLegend: activeLegends.slice(0, 1)[0], + selectedLegends: activeLegends, + }; + + const commonProps = { + legendProps: multiSelectLegendProps, + componentRef: chartRef, + calloutProps: { layerProps: { eventBubblingEnabled: true } }, }; const checkAndRenderChart = ( @@ -132,16 +138,14 @@ export const DeclarativeChart: React.FunctionComponent = const isXNumber = isNumberArray(xValues); const isXMonth = isMonthArray(xValues); if (isXDate || isXNumber) { - const chartProps = { + const chartProps: ILineChartProps | IAreaChartProps = { ...transformPlotlyJsonToScatterChartProps( { data: plotlyInput.data, layout: plotlyInput.layout }, isAreaChart, colorMap, isDarkTheme, ), - legendProps, - componentRef: chartRef, - calloutProps: { layerProps: { eventBubblingEnabled: true } }, + ...commonProps, }; return renderChartJsx(chartProps); } else if (isXMonth) { @@ -149,16 +153,14 @@ export const DeclarativeChart: React.FunctionComponent = ...dataPoint, x: updateXValues(dataPoint.x), })); - const chartProps = { + const chartProps: ILineChartProps | IAreaChartProps = { ...transformPlotlyJsonToScatterChartProps( { data: updatedData, layout: plotlyInput.layout }, isAreaChart, colorMap, isDarkTheme, ), - legendProps, - componentRef: chartRef, - calloutProps: { layerProps: { eventBubblingEnabled: true } }, + ...commonProps, }; return renderChartJsx(chartProps); } @@ -167,9 +169,7 @@ export const DeclarativeChart: React.FunctionComponent = return ( ); }; @@ -192,32 +192,16 @@ export const DeclarativeChart: React.FunctionComponent = [exportAsImage], ); - const multiSelectLegendProps = { - ...legendProps, - canSelectMultipleLegends: true, - selectedLegends: activeLegends, - }; - switch (plotlyInput.data[0].type) { case 'pie': - return ( - - ); + return ; case 'bar': const orientation = plotlyInput.data[0].orientation; if (orientation === 'h') { return ( ); } else { @@ -225,18 +209,14 @@ export const DeclarativeChart: React.FunctionComponent = return ( ); } return ( ); } @@ -246,81 +226,38 @@ export const DeclarativeChart: React.FunctionComponent = ); const renderChartJsx = (chartProps: ILineChartProps | IAreaChartProps) => { if (isAreaChart) { - return ( - - ); + return ; } - return ( - - ); + return ; }; return checkAndRenderChart(renderChartJsx, isAreaChart); case 'heatmap': - return ( - - ); + return ; case 'sankey': return ( - + ); case 'indicator': if (plotlyInput.data?.[0]?.mode?.includes('gauge')) { return ( - + ); } - return
Unsupported Schema
; + throw new Error(`Unsupported chart - type: ${plotlyInput.data[0]?.type}, mode: ${plotlyInput.data[0]?.mode}`); case 'histogram': return ( - + ); default: const xValues = (plotlyInput.data[0] as PlotData).x; const yValues = (plotlyInput.data[0] as PlotData).y; if (xValues && yValues && xValues.length > 0 && yValues.length > 0) { const renderLineChartJsx = (chartProps: ILineChartProps) => { - return ( - - ); + return ; }; return checkAndRenderChart(renderLineChartJsx); } - throw new Error('Unsupported chart schema'); + throw new Error(`Unsupported chart type :${plotlyInput.data[0]?.type}`); } }); DeclarativeChart.displayName = 'DeclarativeChart'; From 03455a430383e4650817c7869230f3739df7ea59 Mon Sep 17 00:00:00 2001 From: krkshitij <110246001+krkshitij@users.noreply.github.com> Date: Fri, 17 Jan 2025 14:21:54 +0530 Subject: [PATCH 13/20] fix(react-charting): resolve bugs in declarative chart (#33564) --- ...-b50824ac-5ec3-43fe-af53-76307680fb75.json | 7 +++++ .../DeclarativeChart/DeclarativeChart.tsx | 7 ++++- .../DeclarativeChart/PlotlySchemaAdapter.ts | 30 +++++++++++++------ .../DeclarativeChartRTL.test.tsx.snap | 4 +-- .../PlotlySchemaAdapterUT.test.tsx.snap | 11 +++++-- .../VerticalBarChartRTL.test.tsx.snap | 4 +-- .../VerticalStackedBarChart.base.tsx | 2 +- .../react-charting/src/utilities/utilities.ts | 11 ++----- 8 files changed, 50 insertions(+), 26 deletions(-) create mode 100644 change/@fluentui-react-charting-b50824ac-5ec3-43fe-af53-76307680fb75.json diff --git a/change/@fluentui-react-charting-b50824ac-5ec3-43fe-af53-76307680fb75.json b/change/@fluentui-react-charting-b50824ac-5ec3-43fe-af53-76307680fb75.json new file mode 100644 index 0000000000000..e18e77a6242e6 --- /dev/null +++ b/change/@fluentui-react-charting-b50824ac-5ec3-43fe-af53-76307680fb75.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: resolve bugs in declarative chart", + "packageName": "@fluentui/react-charting", + "email": "110246001+krkshitij@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx b/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx index 3436c28cafe21..b10fd587ca17c 100644 --- a/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx +++ b/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx @@ -22,6 +22,7 @@ import { transformPlotlyJsonToGaugeProps, transformPlotlyJsonToGVBCProps, transformPlotlyJsonToVBCProps, + isLineData, } from './PlotlySchemaAdapter'; import { LineChart, ILineChartProps } from '../LineChart/index'; import { HorizontalBarChartWithAxis } from '../HorizontalBarChartWithAxis/index'; @@ -178,6 +179,7 @@ export const DeclarativeChart: React.FunctionComponent = (opts?: IImageExportOptions) => { return toImage(chartRef.current?.chartContainer, { background: theme.semanticColors.bodyBackground, + scale: 3, ...opts, }); }, @@ -205,7 +207,10 @@ export const DeclarativeChart: React.FunctionComponent = /> ); } else { - if (['group', 'overlay'].includes(plotlySchema?.layout?.barmode)) { + const containsLines = plotlyInput.data.some( + series => series.type === 'scatter' || isLineData(series as Partial), + ); + if (['group', 'overlay'].includes(plotlySchema?.layout?.barmode) && !containsLines) { return ( !isNaN(Date.parse(value)); const isNumber = (value: any): boolean => !isNaN(parseFloat(value)) && isFinite(value); @@ -49,24 +51,24 @@ const isMonth = (possiblyMonthValue: any, presentYear: number): boolean => { }; const isArrayOfType = ( - data: Datum[] | Datum[][] | TypedArray, + plotCoordinates: Datum[] | Datum[][] | TypedArray | undefined, typeCheck: (datum: any, ...args: any[]) => boolean, ...args: any[] ): boolean => { - if (!isArrayOrTypedArray(data)) { + if (!isArrayOrTypedArray(plotCoordinates)) { return false; } - if (data.length === 0) { + if (plotCoordinates!.length === 0) { return false; } - if (Array.isArray(data[0])) { + if (Array.isArray(plotCoordinates![0])) { // Handle 2D array - return (data as Datum[][]).every(innerArray => innerArray.every(datum => typeCheck(datum, ...args))); + return (plotCoordinates as Datum[][]).every(innerArray => innerArray.every(datum => typeCheck(datum, ...args))); } else { // Handle 1D array - return (data as Datum[]).every(datum => typeCheck(datum, ...args)); + return (plotCoordinates as Datum[]).every(datum => typeCheck(datum, ...args)); } }; @@ -83,6 +85,16 @@ export const isMonthArray = (data: Datum[] | Datum[][] | TypedArray): boolean => return isArrayOfType(data, isMonth, presentYear); }; +export const isLineData = (data: Partial): boolean => { + return ( + !SUPPORTED_PLOT_TYPES.includes(`${data.type}`) && + Array.isArray(data.x) && + isArrayOfType(data.y, (value: any) => typeof value === 'number') && + data.x.length > 0 && + data.x.length === data.y!.length + ); +}; + const invalidate2Dseries = (series: PlotData, chartType: string): void => { if (series.x?.length > 0 && Array.isArray(series.x[0])) { throw new Error(`transform to ${chartType}:: 2D x array not supported`); @@ -252,21 +264,21 @@ export const transformPlotlyJsonToVSBCProps = ( } const legend: string = getLegend(series, index1); const yVal: number = (series.y?.[index2] as number) ?? 0; - if (series.type === 'bar' || series.type === 'scatter' || !!fallbackVSBC) { + if (series.type === 'bar') { const color = getColor(legend, colorMap, isDarkTheme); mapXToDataPoints[x].chartData.push({ legend, data: yVal, color, }); - } /* else if (series.type === 'line') { //ToDo - Fix this as series.type cannot be line type + } else if (series.type === 'scatter' || isLineData(series) || !!fallbackVSBC) { const color = getColor(legend, colorMap, isDarkTheme); mapXToDataPoints[x].lineData!.push({ legend, y: yVal, color, }); - } */ + } yMaxValue = Math.max(yMaxValue, yVal); }); diff --git a/packages/charts/react-charting/src/components/DeclarativeChart/__snapshots__/DeclarativeChartRTL.test.tsx.snap b/packages/charts/react-charting/src/components/DeclarativeChart/__snapshots__/DeclarativeChartRTL.test.tsx.snap index fa793af1fa80c..eeef3bf4f7cde 100644 --- a/packages/charts/react-charting/src/components/DeclarativeChart/__snapshots__/DeclarativeChartRTL.test.tsx.snap +++ b/packages/charts/react-charting/src/components/DeclarativeChart/__snapshots__/DeclarativeChartRTL.test.tsx.snap @@ -4142,7 +4142,7 @@ exports[`DeclarativeChart Should render gaugechart in DeclarativeChart 1`] = ` transform="rotate(151.2, 0, 0)" > - 420 + 84%
- 10,000 + 10000 - 25,000 + 25000 diff --git a/packages/charts/react-charting/src/components/VerticalStackedBarChart/VerticalStackedBarChart.base.tsx b/packages/charts/react-charting/src/components/VerticalStackedBarChart/VerticalStackedBarChart.base.tsx index bae69324657ec..1fd8e2d723c10 100644 --- a/packages/charts/react-charting/src/components/VerticalStackedBarChart/VerticalStackedBarChart.base.tsx +++ b/packages/charts/react-charting/src/components/VerticalStackedBarChart/VerticalStackedBarChart.base.tsx @@ -1254,7 +1254,7 @@ export class VerticalStackedBarChartBase return !( this.props.data && this.props.data.length > 0 && - this.props.data.filter(item => item.chartData.length === 0).length === 0 + this.props.data.some(item => item.chartData.length > 0 || (item.lineData && item.lineData.length > 0)) ); } diff --git a/packages/charts/react-charting/src/utilities/utilities.ts b/packages/charts/react-charting/src/utilities/utilities.ts index 735024e852f98..9396c424fdce0 100644 --- a/packages/charts/react-charting/src/utilities/utilities.ts +++ b/packages/charts/react-charting/src/utilities/utilities.ts @@ -312,19 +312,12 @@ export function createStringXAxis( .range([domainNRangeValues.rStartValue, domainNRangeValues.rEndValue]) .paddingInner(typeof xAxisInnerPadding !== 'undefined' ? xAxisInnerPadding : xAxisPadding) .paddingOuter(typeof xAxisOuterPadding !== 'undefined' ? xAxisOuterPadding : xAxisPadding); - const xAxis = d3AxisBottom(xAxisScale) - .tickSize(xAxistickSize) - .tickPadding(tickPadding) - .ticks(xAxisCount) - .tickFormat((x: string, index: number) => { - return convertToLocaleString(dataset[index], culture) as string; - }); + const xAxis = d3AxisBottom(xAxisScale).tickSize(xAxistickSize).tickPadding(tickPadding).ticks(xAxisCount); if (xAxisParams.xAxisElement) { d3Select(xAxisParams.xAxisElement).call(xAxis).selectAll('text').attr('aria-hidden', 'true'); } - const tickValues = dataset.map(xAxis.tickFormat()!); - return { xScale: xAxisScale, tickValues }; + return { xScale: xAxisScale, tickValues: dataset }; } /** From fac72805b9d2bde1be657d8931dff61206638b89 Mon Sep 17 00:00:00 2001 From: Anush Gupta <74965306+Anush2303@users.noreply.github.com> Date: Fri, 17 Jan 2025 14:47:15 +0530 Subject: [PATCH 14/20] fix(declarative charts): Add check in Date parsing method (#33674) --- .../DeclarativeChart/PlotlySchemaAdapter.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts b/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts index 5844337d7f096..8b3b6755b72e2 100644 --- a/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts +++ b/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts @@ -37,15 +37,21 @@ interface ISecondaryYAxisValues { secondaryYScaleOptions?: { yMinValue?: number; yMaxValue?: number }; } +const isDate = (value: any): boolean => { + const parsedDate = new Date(Date.parse(value)); + if (isNaN(parsedDate.getTime())) { + return false; + } + const parsedYear = parsedDate.getFullYear(); + const yearInString = /\b\d{4}\b/.test(value); + if (!yearInString && (parsedYear === 2000 || parsedYear === 2001)) { + return false; + } + return true; +}; const SUPPORTED_PLOT_TYPES = ['pie', 'bar', 'scatter', 'heatmap', 'sankey', 'indicator', 'histogram']; - -const isDate = (value: any): boolean => !isNaN(Date.parse(value)); const isNumber = (value: any): boolean => !isNaN(parseFloat(value)) && isFinite(value); -// const isDate = (datum: any): datum is Date => { -// return datum instanceof Date; -// }; - const isMonth = (possiblyMonthValue: any, presentYear: number): boolean => { return isDate(`${possiblyMonthValue} 01, ${presentYear}`); }; From 379df8eb3df3efb631c31123487a43c05e791e0d Mon Sep 17 00:00:00 2001 From: "Atishay Jain (from Dev Box)" Date: Sat, 18 Jan 2025 17:30:58 +0530 Subject: [PATCH 15/20] Fix gauge value label --- .../src/components/DeclarativeChart/PlotlySchemaAdapter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts b/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts index 8b3b6755b72e2..95abcfc3a7c6d 100644 --- a/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts +++ b/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts @@ -725,7 +725,7 @@ export const transformPlotlyJsonToGaugeProps = ( // range values can be null minValue: typeof firstData.gauge?.axis?.range?.[0] === 'number' ? firstData.gauge?.axis?.range?.[0] : undefined, maxValue: typeof firstData.gauge?.axis?.range?.[1] === 'number' ? firstData.gauge?.axis?.range?.[1] : undefined, - //chartValueFormat: () => firstData.value, ToDo: fix this + chartValueFormat: () => firstData.value?.toString() ?? '', width: input.layout?.width ?? 440, height: input.layout?.height ?? 220, styles, From 5309685e7664b51c8f4766f3f6219a5dbe2a9fdf Mon Sep 17 00:00:00 2001 From: Anush Gupta <74965306+Anush2303@users.noreply.github.com> Date: Mon, 20 Jan 2025 15:33:27 +0530 Subject: [PATCH 16/20] feat(declarative-chart): Add default colorScale in heatmap chart (#33686) --- .../DeclarativeChart/PlotlySchemaAdapter.ts | 20 +++++++++++++------ .../DeclarativeChartRTL.test.tsx.snap | 4 ++-- .../PlotlySchemaAdapterUT.test.tsx.snap | 1 + 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts b/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts index 95abcfc3a7c6d..b25c7424c3732 100644 --- a/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts +++ b/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts @@ -25,7 +25,7 @@ import { IHorizontalBarChartWithAxisProps } from '../HorizontalBarChartWithAxis/ import { ILineChartProps } from '../LineChart/index'; import { IAreaChartProps } from '../AreaChart/index'; import { IHeatMapChartProps } from '../HeatMapChart/index'; -import { DataVizPalette, getNextColor } from '../../utilities/colors'; +import { DataVizPalette, getColorFromToken, getNextColor } from '../../utilities/colors'; import { GaugeChartVariant, IGaugeChartProps, IGaugeChartSegment } from '../GaugeChart/index'; import { IGroupedVerticalBarChartProps } from '../GroupedVerticalBarChart/index'; import { IVerticalBarChartProps } from '../VerticalBarChart/index'; @@ -585,13 +585,21 @@ export const transformPlotlyJsonToHeatmapProps = (input: PlotlySchema): IHeatMap value: 0, }; - // Convert normalized values to actual values - const domainValuesForColorScale: number[] = firstData.colorscale + // Initialize domain and range to default values + const defaultDomain = [zMin, zMax]; + const defaultRange = [ + getColorFromToken(DataVizPalette.color1), + getColorFromToken(DataVizPalette.color2), + getColorFromToken(DataVizPalette.color3), + ]; + const domainValuesForColorScale: number[] = Array.isArray(firstData.colorscale) ? (firstData.colorscale as Array<[number, string]>).map(arr => arr[0] * (zMax - zMin) + zMin) - : []; - const rangeValuesForColorScale: string[] = firstData.colorscale + : defaultDomain; + + const rangeValuesForColorScale: string[] = Array.isArray(firstData.colorscale) ? (firstData.colorscale as Array<[number, string]>).map(arr => arr[1]) - : []; + : defaultRange; + const { chartTitle, xAxisTitle, yAxisTitle } = getTitles(input.layout); return { diff --git a/packages/charts/react-charting/src/components/DeclarativeChart/__snapshots__/DeclarativeChartRTL.test.tsx.snap b/packages/charts/react-charting/src/components/DeclarativeChart/__snapshots__/DeclarativeChartRTL.test.tsx.snap index eeef3bf4f7cde..fa793af1fa80c 100644 --- a/packages/charts/react-charting/src/components/DeclarativeChart/__snapshots__/DeclarativeChartRTL.test.tsx.snap +++ b/packages/charts/react-charting/src/components/DeclarativeChart/__snapshots__/DeclarativeChartRTL.test.tsx.snap @@ -4142,7 +4142,7 @@ exports[`DeclarativeChart Should render gaugechart in DeclarativeChart 1`] = ` transform="rotate(151.2, 0, 0)" > - 84% + 420 Date: Mon, 20 Jan 2025 16:15:56 +0530 Subject: [PATCH 17/20] fix: [BUG 12161] handle scatter plot with markers mode in plotly (#33688) --- .../src/components/DeclarativeChart/DeclarativeChart.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx b/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx index b10fd587ca17c..e8722b441ffea 100644 --- a/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx +++ b/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx @@ -226,6 +226,9 @@ export const DeclarativeChart: React.FunctionComponent = ); } case 'scatter': + if (plotlyInput.data[0].mode === 'markers') { + throw new Error(`Unsupported chart type :${plotlyInput.data[0]?.type} with mode ${plotlyInput.data[0]?.mode}`); + } const isAreaChart = plotlyInput.data.some( (series: PlotData) => series.fill === 'tonexty' || series.fill === 'tozeroy', ); From 3e4fdb79ba80a0b6470cff23bcd588909b4d4339 Mon Sep 17 00:00:00 2001 From: srmukher <120183316+srmukher@users.noreply.github.com> Date: Mon, 20 Jan 2025 16:16:06 +0530 Subject: [PATCH 18/20] fix: [BUG 12159] default legend selection for Horizontal Bar Chart (#33687) --- .../HorizontalBarChartWithAxis.base.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/charts/react-charting/src/components/HorizontalBarChartWithAxis/HorizontalBarChartWithAxis.base.tsx b/packages/charts/react-charting/src/components/HorizontalBarChartWithAxis/HorizontalBarChartWithAxis.base.tsx index cce810f303e16..9bd1d116c7bc8 100644 --- a/packages/charts/react-charting/src/components/HorizontalBarChartWithAxis/HorizontalBarChartWithAxis.base.tsx +++ b/packages/charts/react-charting/src/components/HorizontalBarChartWithAxis/HorizontalBarChartWithAxis.base.tsx @@ -91,7 +91,9 @@ export class HorizontalBarChartWithAxisBase color: '', dataForHoverCard: 0, isCalloutVisible: false, - isLegendSelected: props.legendProps?.selectedLegend !== undefined, + isLegendSelected: + (props.legendProps?.selectedLegends && props.legendProps.selectedLegends.length > 0) || + props.legendProps?.selectedLegend !== undefined, isLegendHovered: false, refSelected: null, selectedLegendTitle: props.legendProps?.selectedLegend ?? '', From 465a1cd45b970e81d5a60b7b03c04d661e1df5ce Mon Sep 17 00:00:00 2001 From: "Atishay Jain (from Dev Box)" Date: Mon, 20 Jan 2025 16:39:42 +0530 Subject: [PATCH 19/20] Format error --- .../src/components/DeclarativeChart/DeclarativeChart.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx b/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx index e8722b441ffea..728f5ddfbbf79 100644 --- a/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx +++ b/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx @@ -227,7 +227,7 @@ export const DeclarativeChart: React.FunctionComponent = } case 'scatter': if (plotlyInput.data[0].mode === 'markers') { - throw new Error(`Unsupported chart type :${plotlyInput.data[0]?.type} with mode ${plotlyInput.data[0]?.mode}`); + throw new Error(`Unsupported chart - type :${plotlyInput.data[0]?.type}, mode: ${plotlyInput.data[0]?.mode}`); } const isAreaChart = plotlyInput.data.some( (series: PlotData) => series.fill === 'tonexty' || series.fill === 'tozeroy', From a19d189f045db9eae1eadc5e9210fa36cffc867e Mon Sep 17 00:00:00 2001 From: "Atishay Jain (from Dev Box)" Date: Mon, 20 Jan 2025 16:56:30 +0530 Subject: [PATCH 20/20] Update snapshot --- .../__snapshots__/PlotlySchemaAdapterUT.test.tsx.snap | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/charts/react-charting/src/components/DeclarativeChart/__snapshots__/PlotlySchemaAdapterUT.test.tsx.snap b/packages/charts/react-charting/src/components/DeclarativeChart/__snapshots__/PlotlySchemaAdapterUT.test.tsx.snap index 56d9c08d9486f..6faa14a576ee1 100644 --- a/packages/charts/react-charting/src/components/DeclarativeChart/__snapshots__/PlotlySchemaAdapterUT.test.tsx.snap +++ b/packages/charts/react-charting/src/components/DeclarativeChart/__snapshots__/PlotlySchemaAdapterUT.test.tsx.snap @@ -3326,9 +3326,9 @@ Object { }, ], }, + "mode": "tonexty", "secondaryYAxistitle": undefined, "secondaryYScaleOptions": undefined, - "mode": "tonexty", "supportNegativeData": true, "xAxisTitle": "", "yAxisTitle": "", @@ -4560,9 +4560,9 @@ Object { }, ], }, + "mode": "tonexty", "secondaryYAxistitle": undefined, "secondaryYScaleOptions": undefined, - "mode": "tonexty", "supportNegativeData": true, "xAxisTitle": "", "yAxisTitle": "",