From 6641976e98a1a8b08292d2da2c6e1dcf01d09a07 Mon Sep 17 00:00:00 2001 From: ramneet-persistent <105915936+ramneet-persistent@users.noreply.github.com> Date: Sat, 24 Sep 2022 02:16:38 +0530 Subject: [PATCH] Pie chart 2way sync (#1029) * mode bug fixed code: added, 2 way changes testing: in progress Signed-off-by: Ramneet Chopra * pie two sync done Signed-off-by: Ramneet Chopra * yarn test Signed-off-by: Ramneet Chopra * pr feedback Signed-off-by: Ramneet Chopra * spaces added in between lines Signed-off-by: Ramneet Chopra * usememo removed from xlables Signed-off-by: Ramneet Chopra Signed-off-by: Ramneet Chopra --- common/constants/explorer.ts | 39 ++++- common/types/explorer.ts | 4 + .../__snapshots__/config_panel.test.tsx.snap | 28 ++-- .../event_analytics/utils/utils.tsx | 20 ++- .../visualizations/charts/pie/pie.tsx | 154 +++++++++--------- .../visualizations/charts/pie/pie_type.ts | 62 +++---- 6 files changed, 172 insertions(+), 135 deletions(-) diff --git a/common/constants/explorer.ts b/common/constants/explorer.ts index 564c68167..d801ff9fb 100644 --- a/common/constants/explorer.ts +++ b/common/constants/explorer.ts @@ -120,7 +120,16 @@ export const AGGREGATION_OPTIONS = [ ]; // numeric fields type for metrics -export const numericalTypes = ['float', 'double', 'bigint', 'long', 'octet', 'short', 'byte', 'integer']; +export const numericalTypes = [ + 'float', + 'double', + 'bigint', + 'long', + 'octet', + 'short', + 'byte', + 'integer', +]; // Data table constants export const GRID_HEADER_COLUMN_MAX_WIDTH = '150px'; export const GRID_PAGE_RANGE_DISPLAY = 5; @@ -136,13 +145,13 @@ export const HEADER_HEIGHT = 35; // gauge chart default parameters export interface DefaultGaugeChartParametersProps { - GaugeTitleSize: number, - DisplayDefaultGauges: number, - OrientationDefault: string, - TickLength: number, - LegendPlacement: string, - ThresholdsMaxLimit: number -}; + GaugeTitleSize: number; + DisplayDefaultGauges: number; + OrientationDefault: string; + TickLength: number; + LegendPlacement: string; + ThresholdsMaxLimit: number; +} export const DefaultGaugeChartParameters: DefaultGaugeChartParametersProps = { GaugeTitleSize: 14, @@ -150,5 +159,17 @@ export const DefaultGaugeChartParameters: DefaultGaugeChartParametersProps = { OrientationDefault: 'h', TickLength: 5, LegendPlacement: 'center', - ThresholdsMaxLimit: 1 + ThresholdsMaxLimit: 1, +}; + +// pie chart default parameters +export const PLOTLY_PIE_COLUMN_NUMBER = 2; +export const PIE_XAXIS_GAP = 0.2; +export const PIE_YAXIS_GAP = 0.1; +export interface DefaultPieChartParametersProps { + DefaultMode: string; } + +export const DefaultPieChartParameters: DefaultPieChartParametersProps = { + DefaultMode: 'pie', +}; diff --git a/common/types/explorer.ts b/common/types/explorer.ts index dade11d67..2c34b30cd 100644 --- a/common/types/explorer.ts +++ b/common/types/explorer.ts @@ -322,3 +322,7 @@ export interface DataConfigPanelProps { visualizations: IVisualizationContainerProps; qm?: QueryManager; } +export interface GetTooltipHoverInfoType { + tooltipMode: string; + tooltipText: string; +} diff --git a/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap b/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap index 324b855b4..3a5752339 100644 --- a/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap +++ b/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap @@ -1422,25 +1422,25 @@ exports[`Config panel component Renders config panel with visualization data 1`] "name": "Chart styles", "schemas": Array [ Object { - "component": null, - "defaultState": Array [ - Object { - "label": "Pie", - "modeId": "pie", - "name": "Pie", - }, - ], + "component": [Function], + "eleType": "buttons", "isSingleSelection": true, "mapTo": "mode", "name": "Mode", "props": Object { - "dropdownList": Array [ + "defaultSelections": Array [ Object { - "modeId": "pie", + "id": "pie", + "name": "Pie", + }, + ], + "options": Array [ + Object { + "id": "pie", "name": "Pie", }, Object { - "modeId": "donut", + "id": "donut", "name": "Donut", }, ], @@ -1493,12 +1493,10 @@ exports[`Config panel component Renders config panel with visualization data 1`] "id": "pie", "label": "Pie", "legendposition": "v", + "mode": "pie", "name": "pie", - "selection": Object { - "dataLoss": "nothing", - }, "seriesaxis": "yaxis", - "showlegend": true, + "showlegend": "show", "type": "pie", "visconfig": Object { "config": Object { diff --git a/public/components/event_analytics/utils/utils.tsx b/public/components/event_analytics/utils/utils.tsx index dafc1e2f2..41fe02467 100644 --- a/public/components/event_analytics/utils/utils.tsx +++ b/public/components/event_analytics/utils/utils.tsx @@ -8,7 +8,11 @@ import { uniqueId } from 'lodash'; import React from 'react'; import moment from 'moment'; import dateMath from '@elastic/datemath'; -import { IExplorerFields, IField } from '../../../../common/types/explorer'; +import { + IExplorerFields, + IField, + GetTooltipHoverInfoType, +} from '../../../../common/types/explorer'; import { DocViewRow, IDocType } from '../explorer/events_views'; import { HttpStart } from '../../../../../../src/core/public'; import PPLService from '../../../services/requests/ppl'; @@ -121,8 +125,8 @@ export const populateDataGrid = ( )} {explorerFields?.queriedFields && - explorerFields?.queriedFields?.length > 0 && - explorerFields.selectedFields?.length === 0 ? null : ( + explorerFields?.queriedFields?.length > 0 && + explorerFields.selectedFields?.length === 0 ? null : ( {header2}{body2} @@ -354,3 +358,13 @@ export const fetchConfigObject = (editor: string, propsOptions: any) => { return null; } }; + +export const getTooltipHoverInfo = ({ tooltipMode, tooltipText }: GetTooltipHoverInfoType) => { + if (tooltipMode === 'hidden') { + return 'none'; + } + if (tooltipText === undefined) { + return 'all'; + } + return tooltipText; +}; diff --git a/public/components/visualizations/charts/pie/pie.tsx b/public/components/visualizations/charts/pie/pie.tsx index 64b1a5cf2..89d1657fe 100644 --- a/public/components/visualizations/charts/pie/pie.tsx +++ b/public/components/visualizations/charts/pie/pie.tsx @@ -4,10 +4,17 @@ */ import React, { useMemo } from 'react'; -import { take, isEmpty } from 'lodash'; +import { isEmpty, find } from 'lodash'; import { Plt } from '../../plotly/plot'; -import { DEFAULT_PALETTE, HEX_CONTRAST_COLOR } from '../../../../../common/constants/colors'; import { EmptyPlaceholder } from '../../../event_analytics/explorer/visualizations/shared_components/empty_placeholder'; +import { getTooltipHoverInfo } from '../../../event_analytics/utils/utils'; +import { ConfigListEntry } from '../../../../../common/types/explorer'; +import { DEFAULT_PALETTE, HEX_CONTRAST_COLOR } from '../../../../../common/constants/colors'; +import { + PLOTLY_PIE_COLUMN_NUMBER, + PIE_YAXIS_GAP, + PIE_XAXIS_GAP, +} from '../../../../../common/constants/explorer'; export const Pie = ({ visualizations, layout, config }: any) => { const { vis } = visualizations; @@ -15,70 +22,63 @@ export const Pie = ({ visualizations, layout, config }: any) => { data, metadata: { fields }, } = visualizations.data.rawVizData; - const { defaultAxes } = visualizations.data; - const { dataConfig = {}, layoutConfig = {} } = visualizations?.data?.userConfigs; - const xaxis = dataConfig?.dimensions ? dataConfig.dimensions.filter((item) => item.label) : []; - const yaxis = dataConfig?.metrics ? dataConfig.metrics.filter((item) => item.label) : []; - const type = dataConfig?.chartStyles?.mode ? dataConfig?.chartStyles?.mode[0]?.modeId : 'pie'; - const lastIndex = fields.length - 1; - const colorTheme = dataConfig?.chartStyles?.colorTheme - ? dataConfig?.chartStyles?.colorTheme - : { name: DEFAULT_PALETTE }; - const showLegend = dataConfig?.legend?.showLegend === 'hidden' ? false : vis.showlegend; - const legendPosition = dataConfig?.legend?.position || vis.legendposition; - const legendSize = dataConfig?.legend?.size || vis.legendSize; - const labelSize = dataConfig?.chartStyles?.labelSize || vis.labelSize; - const tooltipMode = - dataConfig?.tooltipOptions?.tooltipMode !== undefined - ? dataConfig.tooltipOptions.tooltipMode - : 'show'; - const tooltipText = - dataConfig?.tooltipOptions?.tooltipText !== undefined - ? dataConfig.tooltipOptions.tooltipText - : 'all'; - - if (isEmpty(xaxis) || isEmpty(yaxis)) - return ; + const { + dataConfig: { + chartStyles = {}, + dimensions = [], + metrics = [], + span = {}, + legend = {}, + panelOptions = {}, + tooltipOptions = {}, + }, + layoutConfig = {}, + } = visualizations?.data?.userConfigs; + const type = chartStyles.mode || vis.mode; + const colorTheme = chartStyles.colorTheme ? chartStyles.colorTheme : { name: DEFAULT_PALETTE }; + const showLegend = legend.showLegend === 'hidden' ? false : vis.showlegend; + const legendSize = legend.size || vis.legendSize; + const labelSize = chartStyles.labelSize || vis.labelSize; + const title = panelOptions.title || layoutConfig.layout?.title || ''; + const timestampField = find(fields, (field) => field.type === 'timestamp'); - let valueSeries; - if (!isEmpty(xaxis) && !isEmpty(yaxis)) { - valueSeries = [...yaxis]; + /** + * determine x axis + */ + let xaxes: ConfigListEntry[]; + if (span && span.time_field && timestampField) { + xaxes = [timestampField, ...dimensions]; } else { - valueSeries = defaultAxes.yaxis || take(fields, lastIndex > 0 ? lastIndex : 1); + xaxes = dimensions; + } + + if (isEmpty(xaxes) || isEmpty(metrics)) { + return ; } const invertHex = (hex: string) => (Number(`0x1${hex}`) ^ HEX_CONTRAST_COLOR).toString(16).substr(1).toUpperCase(); - const createLegendLabels = (dimLabels: string[], xaxisLables: string[]) => { - return dimLabels.map((label: string, index: number) => { - return [xaxisLables[index], label].join(','); - }); - }; - - const labelsOfXAxis = useMemo(() => { - let legendLabels = []; - if (xaxis.length > 0) { - let dimLabelsArray = data[xaxis[0].label]; - for (let i = 0; i < xaxis.length - 1; i++) { - dimLabelsArray = createLegendLabels(dimLabelsArray, data[xaxis[i + 1].label]); - } - legendLabels = dimLabelsArray; - } else { - legendLabels = data[fields[lastIndex].name]; + const labelsOfXAxis = xaxes.reduce((prev, cur) => { + if (data[cur.name]) { + if (prev.length === 0) return data[cur.name].flat(); + return prev.map( + (item: string | number, index: number) => `${item}, ${data[cur.name][index]}` + ); } - return legendLabels; - }, [xaxis, data, fields, createLegendLabels]); + }, []); const hexColor = invertHex(colorTheme); + const pies = useMemo( () => - valueSeries.map((field: any, index: number) => { + metrics.map((field: any, index: number) => { + const fieldName = field.alias ? field.alias : `${field.aggregation}(${field.name})`; const marker = colorTheme.name !== DEFAULT_PALETTE ? { marker: { - colors: [...Array(data[field.name].length).fill(colorTheme.childColor)], + colors: [...Array(data[fieldName].length).fill(colorTheme.childColor)], line: { color: hexColor, width: 1, @@ -88,18 +88,22 @@ export const Pie = ({ visualizations, layout, config }: any) => { : undefined; return { labels: labelsOfXAxis, - values: data[field.label], + values: data[fieldName], type: 'pie', - name: field.name, + name: fieldName, hole: type === 'pie' ? 0 : 0.5, - text: field.name, + text: fieldName, textinfo: 'percent', - hoverinfo: tooltipMode === 'hidden' ? 'none' : tooltipText, + hoverinfo: getTooltipHoverInfo({ + tooltipMode: tooltipOptions.tooltipMode, + tooltipText: tooltipOptions.tooltipText, + }), automargin: true, textposition: 'outside', + title: { text: fieldName }, domain: { - row: Math.floor(index / 3), - column: index % 3, + row: Math.floor(index / PLOTLY_PIE_COLUMN_NUMBER), + column: index % PLOTLY_PIE_COLUMN_NUMBER, }, ...marker, outsidetextfont: { @@ -107,38 +111,32 @@ export const Pie = ({ visualizations, layout, config }: any) => { }, }; }), - [valueSeries, valueSeries, data, labelSize, labelsOfXAxis, colorTheme] + [metrics, data, labelSize, labelsOfXAxis, colorTheme] ); - const isAtleastOneFullRow = Math.floor(valueSeries.length / 3) > 0; - - const mergedLayout = useMemo( - () => ({ + const mergedLayout = useMemo(() => { + const isAtleastOneFullRow = Math.floor(metrics.length / PLOTLY_PIE_COLUMN_NUMBER) > 0; + return { grid: { - rows: Math.floor(valueSeries.length / 3) + 1, - columns: isAtleastOneFullRow ? 3 : valueSeries.length, + xgap: PIE_XAXIS_GAP, + ygap: PIE_YAXIS_GAP, + rows: Math.floor(metrics.length / PLOTLY_PIE_COLUMN_NUMBER) + 1, + columns: isAtleastOneFullRow ? PLOTLY_PIE_COLUMN_NUMBER : metrics.length, + pattern: 'independent', }, ...layout, ...(layoutConfig.layout && layoutConfig.layout), - title: dataConfig?.panelOptions?.title || layoutConfig.layout?.title || '', + title, legend: { ...layout.legend, - orientation: legendPosition, - font: { size: legendSize }, + orientation: legend.position || vis.legendposition, + ...(legendSize && { + font: { size: legendSize }, + }), }, showlegend: showLegend, - }), - [ - valueSeries, - isAtleastOneFullRow, - layoutConfig.layout, - dataConfig?.panelOptions?.title, - layoutConfig.layout?.title, - layout.legend, - legendPosition, - legendSize, - ] - ); + }; + }, [metrics, layoutConfig.layout, title, layout.legend]); const mergedConfigs = useMemo( () => ({ diff --git a/public/components/visualizations/charts/pie/pie_type.ts b/public/components/visualizations/charts/pie/pie_type.ts index 51c041e65..c8fea3d51 100644 --- a/public/components/visualizations/charts/pie/pie_type.ts +++ b/public/components/visualizations/charts/pie/pie_type.ts @@ -6,7 +6,6 @@ import { Pie } from './pie'; import { getPlotlySharedConfigs, getPlotlyCategory } from '../shared/shared_configs'; import { LensIconChartPie } from '../../assets/chart_pie'; -import { PLOTLY_COLOR } from '../../../../../common/constants/shared'; import { VizDataPanel } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/default_vis_editor'; import { ConfigEditor } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/json_editor'; import { @@ -14,13 +13,19 @@ import { ConfigChartOptions, ConfigLegend, InputFieldItem, + ButtonGroupItem, } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls'; -import { DEFAULT_PALETTE, PIE_PALETTES } from '../../../../../common/constants/colors'; import { fetchConfigObject } from '../../../../components/event_analytics/utils/utils'; +import { DEFAULT_PALETTE, PIE_PALETTES } from '../../../../../common/constants/colors'; +import { PLOTLY_COLOR, DefaultChartStyles } from '../../../../../common/constants/shared'; +import { DefaultPieChartParameters } from '../../../../../common/constants/explorer'; const sharedConfigs = getPlotlySharedConfigs(); const VIS_CATEGORY = getPlotlyCategory(); +const { ShowLegend, LegendPosition } = DefaultChartStyles; +const { DefaultMode } = DefaultPieChartParameters; + export const createPieTypeDefinition = (params: any) => ({ name: 'pie', type: 'pie', @@ -29,13 +34,11 @@ export const createPieTypeDefinition = (params: any) => ({ fulllabel: 'Pie', icontype: 'visPie', category: VIS_CATEGORY.BASICS, - showlegend: true, - legendposition: 'v', - selection: { - dataLoss: 'nothing', - }, + showlegend: ShowLegend, + legendposition: LegendPosition, categoryaxis: 'xaxis', seriesaxis: 'yaxis', + mode: DefaultMode, icon: LensIconChartPie, editorconfig: { panelTabs: [ @@ -57,10 +60,10 @@ export const createPieTypeDefinition = (params: any) => ({ component: null, props: { options: [ - { name: 'Show', id: 'show' }, + { name: 'Show', id: ShowLegend }, { name: 'Hidden', id: 'hidden' }, ], - defaultSelections: [{ name: 'Show', id: 'show' }], + defaultSelections: [{ name: 'Show', id: ShowLegend }], }, }, { @@ -69,10 +72,10 @@ export const createPieTypeDefinition = (params: any) => ({ component: null, props: { options: [ - { name: 'Right', id: 'v' }, + { name: 'Right', id: LegendPosition }, { name: 'Bottom', id: 'h' }, ], - defaultSelections: [{ name: 'Right', id: 'v' }], + defaultSelections: [{ name: 'Right', id: LegendPosition }], }, }, { @@ -101,15 +104,16 @@ export const createPieTypeDefinition = (params: any) => ({ { name: 'Mode', isSingleSelection: true, - component: null, + component: ButtonGroupItem, + eleType: 'buttons', mapTo: 'mode', props: { - dropdownList: [ - { name: 'Pie', modeId: 'pie' }, - { name: 'Donut', modeId: 'donut' }, + options: [ + { name: 'Pie', id: DefaultMode }, + { name: 'Donut', id: 'donut' }, ], + defaultSelections: [{ name: 'Pie', id: DefaultMode }], }, - defaultState: [{ name: 'Pie', modeId: 'pie', label: 'Pie' }], }, { name: 'Label size', @@ -142,20 +146,18 @@ export const createPieTypeDefinition = (params: any) => ({ visconfig: { layout: { ...sharedConfigs.layout, - ...{ - colorway: PLOTLY_COLOR, - plot_bgcolor: 'rgba(0, 0, 0, 0)', - paper_bgcolor: 'rgba(0, 0, 0, 0)', - xaxis: { - fixedrange: true, - showgrid: false, - visible: true, - }, - yaxis: { - fixedrange: true, - showgrid: false, - visible: true, - }, + colorway: PLOTLY_COLOR, + plot_bgcolor: 'rgba(0, 0, 0, 0)', + paper_bgcolor: 'rgba(0, 0, 0, 0)', + xaxis: { + fixedrange: true, + showgrid: false, + visible: true, + }, + yaxis: { + fixedrange: true, + showgrid: false, + visible: true, }, }, config: {