diff --git a/docs/management/rollups/create_and_manage_rollups.asciidoc b/docs/management/rollups/create_and_manage_rollups.asciidoc index 5887b45901f79..2f9ede62c0b0f 100644 --- a/docs/management/rollups/create_and_manage_rollups.asciidoc +++ b/docs/management/rollups/create_and_manage_rollups.asciidoc @@ -165,7 +165,7 @@ as your source to see both the raw and rolled up data. [role="screenshot"] image::images/management-create-rollup-bar-chart.png[Create visualization of rolled up data] -. Select *Bar vertical stacked* in the chart type dropdown. +. Select *Bar* in the chart type dropdown. . Add the `@timestamp` field to the *Horizontal axis*. diff --git a/packages/kbn-chart-icons/index.ts b/packages/kbn-chart-icons/index.ts index b962185d70589..986360406e38f 100644 --- a/packages/kbn-chart-icons/index.ts +++ b/packages/kbn-chart-icons/index.ts @@ -38,10 +38,18 @@ export { EuiIconLegend, IconRegionMap, IconChartHeatmap, + IconChartGauge, IconChartHorizontalBullet, + IconDonutHoleLarge, + IconDonutHoleMedium, + IconDonutHoleSmall, IconChartVerticalBullet, + IconChartLinearSimple, IconChartGaugeSemiCircle, + IconChartGaugeSemiCircleSimple, IconChartGaugeArc, + IconChartGaugeArcSimple, IconChartGaugeCircle, + IconChartGaugeCircleSimple, IconChartTagcloud, } from './src/assets'; diff --git a/packages/kbn-chart-icons/src/__stories__/icons.stories.tsx b/packages/kbn-chart-icons/src/__stories__/icons.stories.tsx index a04d5bbfda64b..631dce20769f3 100644 --- a/packages/kbn-chart-icons/src/__stories__/icons.stories.tsx +++ b/packages/kbn-chart-icons/src/__stories__/icons.stories.tsx @@ -42,8 +42,12 @@ import { EuiIconLegend, IconRegionMap, IconChartHeatmap, + IconChartGauge, IconChartHorizontalBullet, IconChartVerticalBullet, + IconDonutHoleLarge, + IconDonutHoleMedium, + IconDonutHoleSmall, } from '../..'; export default { @@ -183,10 +187,17 @@ const IconsArray: Array<{ title: 'IconChartHorizontalBullet', Component: IconChartHorizontalBullet, }, + { + title: 'IconChartGauge', + Component: IconChartGauge, + }, { title: 'IconChartVerticalBullet', Component: IconChartVerticalBullet, }, + { title: 'IconDonutHoleLarge', Component: IconDonutHoleLarge }, + { title: 'IconDonutHoleMedium', Component: IconDonutHoleMedium }, + { title: 'IconDonutHoleSmall', Component: IconDonutHoleSmall }, ]; interface RootComponentProps { diff --git a/packages/kbn-chart-icons/src/assets/annotation_icons/circle.tsx b/packages/kbn-chart-icons/src/assets/annotation_icons/circle.tsx index 783e666bbc9e2..c32258b21d18a 100644 --- a/packages/kbn-chart-icons/src/assets/annotation_icons/circle.tsx +++ b/packages/kbn-chart-icons/src/assets/annotation_icons/circle.tsx @@ -8,17 +8,10 @@ import React from 'react'; import { EuiIconProps } from '@elastic/eui'; +import { IconSimpleWrapper } from '../icon_simple_wrapper'; -export const IconCircle = ({ title, titleId, ...props }: Omit) => ( - - {title ? {title} : null} +export const IconCircle = (props: Omit) => ( + - + ); diff --git a/packages/kbn-chart-icons/src/assets/annotation_icons/triangle.tsx b/packages/kbn-chart-icons/src/assets/annotation_icons/triangle.tsx index fda92d5906fcd..7d4fcc1e03db5 100644 --- a/packages/kbn-chart-icons/src/assets/annotation_icons/triangle.tsx +++ b/packages/kbn-chart-icons/src/assets/annotation_icons/triangle.tsx @@ -8,18 +8,10 @@ import React from 'react'; import { EuiIconProps } from '@elastic/eui'; +import { IconSimpleWrapper } from '../icon_simple_wrapper'; -export const IconTriangle = ({ title, titleId, ...props }: Omit) => ( - - {title ? {title} : null} - - +export const IconTriangle = (props: Omit) => ( + + {' '} + ); diff --git a/packages/kbn-chart-icons/src/assets/axis_bottom.tsx b/packages/kbn-chart-icons/src/assets/axis_bottom.tsx index c2ad925a962de..a3f32cade254d 100644 --- a/packages/kbn-chart-icons/src/assets/axis_bottom.tsx +++ b/packages/kbn-chart-icons/src/assets/axis_bottom.tsx @@ -7,26 +7,12 @@ */ import React from 'react'; +import { EuiIconProps } from '@elastic/eui'; +import { IconSimpleWrapper } from './icon_simple_wrapper'; -export const EuiIconAxisBottom = ({ - title, - titleId, - ...props -}: { - title: string; - titleId: string; -}) => ( - - {title ? {title} : null} +export const EuiIconAxisBottom = (props: Omit) => ( + - + ); diff --git a/packages/kbn-chart-icons/src/assets/axis_left.tsx b/packages/kbn-chart-icons/src/assets/axis_left.tsx index e4d4361ecfe75..a971c52a2f232 100644 --- a/packages/kbn-chart-icons/src/assets/axis_left.tsx +++ b/packages/kbn-chart-icons/src/assets/axis_left.tsx @@ -6,28 +6,14 @@ * Side Public License, v 1. */ +import { EuiIconProps } from '@elastic/eui'; import React from 'react'; +import { IconSimpleWrapper } from './icon_simple_wrapper'; -export const EuiIconAxisLeft = ({ - title, - titleId, - ...props -}: { - title: string; - titleId: string; -}) => ( - - {title ? {title} : null} +export const EuiIconAxisLeft = (props: Omit) => ( + - + ); diff --git a/packages/kbn-chart-icons/src/assets/axis_right.tsx b/packages/kbn-chart-icons/src/assets/axis_right.tsx index 8316fbe713388..7bdd232bd76d6 100644 --- a/packages/kbn-chart-icons/src/assets/axis_right.tsx +++ b/packages/kbn-chart-icons/src/assets/axis_right.tsx @@ -6,28 +6,14 @@ * Side Public License, v 1. */ +import { EuiIconProps } from '@elastic/eui'; import React from 'react'; +import { IconSimpleWrapper } from './icon_simple_wrapper'; -export const EuiIconAxisRight = ({ - title, - titleId, - ...props -}: { - title: string; - titleId: string; -}) => ( - - {title ? {title} : null} +export const EuiIconAxisRight = (props: Omit) => ( + - + ); diff --git a/packages/kbn-chart-icons/src/assets/axis_top.tsx b/packages/kbn-chart-icons/src/assets/axis_top.tsx index d78dfcc113aad..2f54e69303e00 100644 --- a/packages/kbn-chart-icons/src/assets/axis_top.tsx +++ b/packages/kbn-chart-icons/src/assets/axis_top.tsx @@ -6,31 +6,17 @@ * Side Public License, v 1. */ +import { EuiIconProps } from '@elastic/eui'; import React from 'react'; +import { IconSimpleWrapper } from './icon_simple_wrapper'; -export const EuiIconAxisTop = ({ - title, - titleId, - ...props -}: { - title: string; - titleId: string; -}) => ( - - {title ? {title} : null} +export const EuiIconAxisTop = (props: Omit) => ( + - + ); diff --git a/packages/kbn-chart-icons/src/assets/chart_area.tsx b/packages/kbn-chart-icons/src/assets/chart_area.tsx index a09a9cab956ad..39176ff4fd881 100644 --- a/packages/kbn-chart-icons/src/assets/chart_area.tsx +++ b/packages/kbn-chart-icons/src/assets/chart_area.tsx @@ -9,18 +9,10 @@ import React from 'react'; import { EuiIconProps } from '@elastic/eui'; import { colors } from './common_styles'; +import { ChartIconWrapper } from './icon_simple_wrapper'; -export const IconChartArea = ({ title, titleId, ...props }: Omit) => ( - - {title ? {title} : null} +export const IconChartArea = (props: Omit) => ( + - + ); diff --git a/packages/kbn-chart-icons/src/assets/chart_area_percentage.tsx b/packages/kbn-chart-icons/src/assets/chart_area_percentage.tsx index 07917b1bcabc2..07698df23feb9 100644 --- a/packages/kbn-chart-icons/src/assets/chart_area_percentage.tsx +++ b/packages/kbn-chart-icons/src/assets/chart_area_percentage.tsx @@ -9,22 +9,10 @@ import React from 'react'; import { EuiIconProps } from '@elastic/eui'; import { colors } from './common_styles'; +import { ChartIconWrapper } from './icon_simple_wrapper'; -export const IconChartAreaPercentage = ({ - title, - titleId, - ...props -}: Omit) => ( - - {title ? {title} : null} +export const IconChartAreaPercentage = (props: Omit) => ( + - + ); diff --git a/packages/kbn-chart-icons/src/assets/chart_area_stacked.tsx b/packages/kbn-chart-icons/src/assets/chart_area_stacked.tsx index 3bfe3cd92b85b..11bc0bd1d87bc 100644 --- a/packages/kbn-chart-icons/src/assets/chart_area_stacked.tsx +++ b/packages/kbn-chart-icons/src/assets/chart_area_stacked.tsx @@ -9,18 +9,10 @@ import React from 'react'; import { EuiIconProps } from '@elastic/eui'; import { colors } from './common_styles'; +import { ChartIconWrapper } from './icon_simple_wrapper'; -export const IconChartAreaStacked = ({ title, titleId, ...props }: Omit) => ( - - {title ? {title} : null} +export const IconChartAreaStacked = (props: Omit) => ( + - + ); diff --git a/packages/kbn-chart-icons/src/assets/chart_bar.tsx b/packages/kbn-chart-icons/src/assets/chart_bar.tsx index f33b83fca5362..42bd671221599 100644 --- a/packages/kbn-chart-icons/src/assets/chart_bar.tsx +++ b/packages/kbn-chart-icons/src/assets/chart_bar.tsx @@ -9,18 +9,10 @@ import React from 'react'; import { EuiIconProps } from '@elastic/eui'; import { colors } from './common_styles'; +import { ChartIconWrapper } from './icon_simple_wrapper'; -export const IconChartBar = ({ title, titleId, ...props }: Omit) => ( - - {title ? {title} : null} +export const IconChartBar = (props: Omit) => ( + - + ); diff --git a/packages/kbn-chart-icons/src/assets/chart_bar_annotations.tsx b/packages/kbn-chart-icons/src/assets/chart_bar_annotations.tsx index 9472cbc0cd762..96aafcd73c603 100644 --- a/packages/kbn-chart-icons/src/assets/chart_bar_annotations.tsx +++ b/packages/kbn-chart-icons/src/assets/chart_bar_annotations.tsx @@ -9,22 +9,10 @@ import React from 'react'; import { EuiIconProps } from '@elastic/eui'; import { colors } from './common_styles'; +import { ChartIconWrapper } from './icon_simple_wrapper'; -export const IconChartBarAnnotations = ({ - title, - titleId, - ...props -}: Omit) => ( - - {title ? {title} : null} +export const IconChartBarAnnotations = (props: Omit) => ( + - + ); diff --git a/packages/kbn-chart-icons/src/assets/chart_bar_horizontal.tsx b/packages/kbn-chart-icons/src/assets/chart_bar_horizontal.tsx index dc113a8ab4ad9..2e24de59c76f8 100644 --- a/packages/kbn-chart-icons/src/assets/chart_bar_horizontal.tsx +++ b/packages/kbn-chart-icons/src/assets/chart_bar_horizontal.tsx @@ -9,22 +9,10 @@ import React from 'react'; import { EuiIconProps } from '@elastic/eui'; import { colors } from './common_styles'; +import { ChartIconWrapper } from './icon_simple_wrapper'; -export const IconChartBarHorizontal = ({ - title, - titleId, - ...props -}: Omit) => ( - - {title ? {title} : null} +export const IconChartBarHorizontal = (props: Omit) => ( + - + ); diff --git a/packages/kbn-chart-icons/src/assets/chart_bar_horizontal_percentage.tsx b/packages/kbn-chart-icons/src/assets/chart_bar_horizontal_percentage.tsx index 91e8f2639bfa5..93ee284c2b7d2 100644 --- a/packages/kbn-chart-icons/src/assets/chart_bar_horizontal_percentage.tsx +++ b/packages/kbn-chart-icons/src/assets/chart_bar_horizontal_percentage.tsx @@ -9,22 +9,10 @@ import React from 'react'; import { EuiIconProps } from '@elastic/eui'; import { colors } from './common_styles'; +import { ChartIconWrapper } from './icon_simple_wrapper'; -export const IconChartBarHorizontalPercentage = ({ - title, - titleId, - ...props -}: Omit) => ( - - {title ? {title} : null} +export const IconChartBarHorizontalPercentage = (props: Omit) => ( + - + ); diff --git a/packages/kbn-chart-icons/src/assets/chart_bar_horizontal_stacked.tsx b/packages/kbn-chart-icons/src/assets/chart_bar_horizontal_stacked.tsx index 6e5e61e386467..1d51577b39b15 100644 --- a/packages/kbn-chart-icons/src/assets/chart_bar_horizontal_stacked.tsx +++ b/packages/kbn-chart-icons/src/assets/chart_bar_horizontal_stacked.tsx @@ -9,22 +9,10 @@ import React from 'react'; import { EuiIconProps } from '@elastic/eui'; import { colors } from './common_styles'; +import { ChartIconWrapper } from './icon_simple_wrapper'; -export const IconChartBarHorizontalStacked = ({ - title, - titleId, - ...props -}: Omit) => ( - - {title ? {title} : null} +export const IconChartBarHorizontalStacked = (props: Omit) => ( + - + ); diff --git a/packages/kbn-chart-icons/src/assets/chart_bar_percentage.tsx b/packages/kbn-chart-icons/src/assets/chart_bar_percentage.tsx index 82a59ebc9be32..e45276bc0b4ba 100644 --- a/packages/kbn-chart-icons/src/assets/chart_bar_percentage.tsx +++ b/packages/kbn-chart-icons/src/assets/chart_bar_percentage.tsx @@ -9,22 +9,10 @@ import React from 'react'; import { EuiIconProps } from '@elastic/eui'; import { colors } from './common_styles'; +import { ChartIconWrapper } from './icon_simple_wrapper'; -export const IconChartBarPercentage = ({ - title, - titleId, - ...props -}: Omit) => ( - - {title ? {title} : null} +export const IconChartBarPercentage = (props: Omit) => ( + - + ); diff --git a/packages/kbn-chart-icons/src/assets/chart_bar_stacked.tsx b/packages/kbn-chart-icons/src/assets/chart_bar_stacked.tsx index 933be6d5dea7a..a97b036ac9519 100644 --- a/packages/kbn-chart-icons/src/assets/chart_bar_stacked.tsx +++ b/packages/kbn-chart-icons/src/assets/chart_bar_stacked.tsx @@ -9,18 +9,10 @@ import React from 'react'; import { EuiIconProps } from '@elastic/eui'; import { colors } from './common_styles'; +import { ChartIconWrapper } from './icon_simple_wrapper'; -export const IconChartBarStacked = ({ title, titleId, ...props }: Omit) => ( - - {title ? {title} : null} +export const IconChartBarStacked = (props: Omit) => ( + - + ); diff --git a/packages/kbn-chart-icons/src/assets/chart_datatable.tsx b/packages/kbn-chart-icons/src/assets/chart_datatable.tsx index 144d6ddb906f5..2cce3cfa12fb1 100644 --- a/packages/kbn-chart-icons/src/assets/chart_datatable.tsx +++ b/packages/kbn-chart-icons/src/assets/chart_datatable.tsx @@ -9,18 +9,10 @@ import React from 'react'; import { EuiIconProps } from '@elastic/eui'; import { colors } from './common_styles'; +import { ChartIconWrapper } from './icon_simple_wrapper'; -export const IconChartDatatable = ({ title, titleId, ...props }: Omit) => ( - - {title ? {title} : null} +export const IconChartDatatable = (props: Omit) => ( + - + ); diff --git a/packages/kbn-chart-icons/src/assets/chart_donut.tsx b/packages/kbn-chart-icons/src/assets/chart_donut.tsx index bd9e3f59e72a1..8be376e3f0d53 100644 --- a/packages/kbn-chart-icons/src/assets/chart_donut.tsx +++ b/packages/kbn-chart-icons/src/assets/chart_donut.tsx @@ -9,18 +9,10 @@ import React from 'react'; import { EuiIconProps } from '@elastic/eui'; import { colors } from './common_styles'; +import { ChartIconWrapper } from './icon_simple_wrapper'; -export const IconChartDonut = ({ title, titleId, ...props }: Omit) => ( - - {title ? {title} : null} +export const IconChartDonut = (props: Omit) => ( + - + ); diff --git a/packages/kbn-chart-icons/src/assets/chart_gauge.tsx b/packages/kbn-chart-icons/src/assets/chart_gauge.tsx new file mode 100644 index 0000000000000..cda10a4efd8d6 --- /dev/null +++ b/packages/kbn-chart-icons/src/assets/chart_gauge.tsx @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import type { EuiIconProps } from '@elastic/eui'; +import { colors } from './common_styles'; +import { ChartIconWrapper } from './icon_simple_wrapper'; + +export const IconChartGauge = (props: Omit) => ( + + + + +); diff --git a/packages/kbn-chart-icons/src/assets/chart_gauge_arc.tsx b/packages/kbn-chart-icons/src/assets/chart_gauge_arc.tsx index a206e5c629407..ef2495356c590 100644 --- a/packages/kbn-chart-icons/src/assets/chart_gauge_arc.tsx +++ b/packages/kbn-chart-icons/src/assets/chart_gauge_arc.tsx @@ -9,28 +9,18 @@ import React from 'react'; import type { EuiIconProps } from '@elastic/eui'; import { colors } from './common_styles'; +import { ChartIconWrapper } from './icon_simple_wrapper'; -export const IconChartGaugeArc = ({ title, titleId, ...props }: Omit) => { - return ( - - {title ? {title} : null} - - - - - - - - ); -}; +export const IconChartGaugeArc = (props: Omit) => ( + + + + + + + + +); diff --git a/packages/kbn-chart-icons/src/assets/chart_gauge_circle.tsx b/packages/kbn-chart-icons/src/assets/chart_gauge_circle.tsx index e89dba7297465..90877cbbe998a 100644 --- a/packages/kbn-chart-icons/src/assets/chart_gauge_circle.tsx +++ b/packages/kbn-chart-icons/src/assets/chart_gauge_circle.tsx @@ -9,27 +9,17 @@ import React from 'react'; import type { EuiIconProps } from '@elastic/eui'; import { colors } from './common_styles'; +import { ChartIconWrapper } from './icon_simple_wrapper'; -export const IconChartGaugeCircle = ({ title, titleId, ...props }: Omit) => { - return ( - - {title ? {title} : null} - - - - - - - ); -}; +export const IconChartGaugeCircle = (props: Omit) => ( + + + + + + + +); diff --git a/packages/kbn-chart-icons/src/assets/chart_gauge_semi_circle.tsx b/packages/kbn-chart-icons/src/assets/chart_gauge_semi_circle.tsx index 912516b3d7118..e22d9495b0f81 100644 --- a/packages/kbn-chart-icons/src/assets/chart_gauge_semi_circle.tsx +++ b/packages/kbn-chart-icons/src/assets/chart_gauge_semi_circle.tsx @@ -9,32 +9,18 @@ import React from 'react'; import type { EuiIconProps } from '@elastic/eui'; import { colors } from './common_styles'; +import { ChartIconWrapper } from './icon_simple_wrapper'; -export const IconChartGaugeSemiCircle = ({ - title, - titleId, - ...props -}: Omit) => { - return ( - - {title ? {title} : null} - - - - - - - - ); -}; +export const IconChartGaugeSemiCircle = (props: Omit) => ( + + + + + + + + +); diff --git a/packages/kbn-chart-icons/src/assets/chart_gauge_simple_icons.tsx b/packages/kbn-chart-icons/src/assets/chart_gauge_simple_icons.tsx new file mode 100644 index 0000000000000..4f139596e4920 --- /dev/null +++ b/packages/kbn-chart-icons/src/assets/chart_gauge_simple_icons.tsx @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import type { EuiIconProps } from '@elastic/eui'; +import { IconSimpleWrapper } from './icon_simple_wrapper'; + +export const IconChartLinearSimple = (props: Omit) => ( + + + +); + +export const IconChartGaugeSemiCircleSimple = (props: Omit) => ( + + + +); + +export const IconChartGaugeCircleSimple = (props: Omit) => ( + + + +); + +export const IconChartGaugeArcSimple = (props: Omit) => ( + + + +); diff --git a/packages/kbn-chart-icons/src/assets/chart_heatmap.tsx b/packages/kbn-chart-icons/src/assets/chart_heatmap.tsx index 0609c7263f202..8dbb58dee6121 100644 --- a/packages/kbn-chart-icons/src/assets/chart_heatmap.tsx +++ b/packages/kbn-chart-icons/src/assets/chart_heatmap.tsx @@ -9,27 +9,17 @@ import { EuiIconProps } from '@elastic/eui'; import React from 'react'; import { colors } from './common_styles'; +import { ChartIconWrapper } from './icon_simple_wrapper'; -export const IconChartHeatmap = ({ title, titleId, ...props }: Omit) => { - return ( - - {title ? {title} : null} - - - - ); -}; +export const IconChartHeatmap = (props: Omit) => ( + + + + +); diff --git a/packages/kbn-chart-icons/src/assets/chart_horizontal_bullet.tsx b/packages/kbn-chart-icons/src/assets/chart_horizontal_bullet.tsx index 0765444e857a6..222b8260c62fb 100644 --- a/packages/kbn-chart-icons/src/assets/chart_horizontal_bullet.tsx +++ b/packages/kbn-chart-icons/src/assets/chart_horizontal_bullet.tsx @@ -9,31 +9,17 @@ import React from 'react'; import type { EuiIconProps } from '@elastic/eui'; import { colors } from './common_styles'; +import { ChartIconWrapper } from './icon_simple_wrapper'; -export const IconChartHorizontalBullet = ({ - title, - titleId, - ...props -}: Omit) => { - return ( - - {title ? {title} : null} - - - - ); -}; +export const IconChartHorizontalBullet = (props: Omit) => ( + + + + +); diff --git a/packages/kbn-chart-icons/src/assets/chart_line.tsx b/packages/kbn-chart-icons/src/assets/chart_line.tsx index afaf31b708621..e8c10085b306b 100644 --- a/packages/kbn-chart-icons/src/assets/chart_line.tsx +++ b/packages/kbn-chart-icons/src/assets/chart_line.tsx @@ -9,18 +9,10 @@ import React from 'react'; import { EuiIconProps } from '@elastic/eui'; import { colors } from './common_styles'; +import { ChartIconWrapper } from './icon_simple_wrapper'; -export const IconChartLine = ({ title, titleId, ...props }: Omit) => ( - - {title ? {title} : null} +export const IconChartLine = (props: Omit) => ( + - + ); diff --git a/packages/kbn-chart-icons/src/assets/chart_metric.tsx b/packages/kbn-chart-icons/src/assets/chart_metric.tsx index 4feb162a4607c..9d53adcff2d93 100644 --- a/packages/kbn-chart-icons/src/assets/chart_metric.tsx +++ b/packages/kbn-chart-icons/src/assets/chart_metric.tsx @@ -9,18 +9,10 @@ import React from 'react'; import { EuiIconProps } from '@elastic/eui'; import { colors } from './common_styles'; +import { ChartIconWrapper } from './icon_simple_wrapper'; -export const IconChartMetric = ({ title, titleId, ...props }: Omit) => ( - - {title ? {title} : null} +export const IconChartMetric = (props: Omit) => ( + - + ); diff --git a/packages/kbn-chart-icons/src/assets/chart_mixed_xy.tsx b/packages/kbn-chart-icons/src/assets/chart_mixed_xy.tsx index af86b96852dc9..511dfd166f49a 100644 --- a/packages/kbn-chart-icons/src/assets/chart_mixed_xy.tsx +++ b/packages/kbn-chart-icons/src/assets/chart_mixed_xy.tsx @@ -9,18 +9,10 @@ import React from 'react'; import { EuiIconProps } from '@elastic/eui'; import { colors } from './common_styles'; +import { ChartIconWrapper } from './icon_simple_wrapper'; -export const IconChartMixedXy = ({ title, titleId, ...props }: Omit) => ( - - {title ? {title} : null} +export const IconChartMixedXy = (props: Omit) => ( + - + ); diff --git a/packages/kbn-chart-icons/src/assets/chart_mosaic.tsx b/packages/kbn-chart-icons/src/assets/chart_mosaic.tsx index 23fe548074d5a..d8b3696d9219c 100644 --- a/packages/kbn-chart-icons/src/assets/chart_mosaic.tsx +++ b/packages/kbn-chart-icons/src/assets/chart_mosaic.tsx @@ -9,18 +9,10 @@ import React from 'react'; import type { EuiIconProps } from '@elastic/eui'; import { colors } from './common_styles'; +import { ChartIconWrapper } from './icon_simple_wrapper'; -export const IconChartMosaic = ({ title, titleId, ...props }: Omit) => ( - - {title ? : null} +export const IconChartMosaic = (props: Omit) => ( + - + ); diff --git a/packages/kbn-chart-icons/src/assets/chart_pie.tsx b/packages/kbn-chart-icons/src/assets/chart_pie.tsx index 16d59e73603ca..3b64e04b6bbb8 100644 --- a/packages/kbn-chart-icons/src/assets/chart_pie.tsx +++ b/packages/kbn-chart-icons/src/assets/chart_pie.tsx @@ -9,18 +9,10 @@ import React from 'react'; import { EuiIconProps } from '@elastic/eui'; import { colors } from './common_styles'; +import { ChartIconWrapper } from './icon_simple_wrapper'; -export const IconChartPie = ({ title, titleId, ...props }: Omit) => ( - - {title ? {title} : null} +export const IconChartPie = (props: Omit) => ( + - + ); diff --git a/packages/kbn-chart-icons/src/assets/chart_tagcloud.tsx b/packages/kbn-chart-icons/src/assets/chart_tagcloud.tsx index 3119841eb8dce..293dfaea01cda 100644 --- a/packages/kbn-chart-icons/src/assets/chart_tagcloud.tsx +++ b/packages/kbn-chart-icons/src/assets/chart_tagcloud.tsx @@ -6,17 +6,13 @@ * Side Public License, v 1. */ -import React, { FunctionComponent } from 'react'; +import React from 'react'; import type { EuiIconProps } from '@elastic/eui'; import { colors } from './common_styles'; +import { ChartIconWrapper } from './icon_simple_wrapper'; -export const IconChartTagcloud: FunctionComponent = ({ - title, - titleId, - ...props -}: Omit) => ( - - {title ? : null} +export const IconChartTagcloud = (props: Omit) => ( + - + ); diff --git a/packages/kbn-chart-icons/src/assets/chart_treemap.tsx b/packages/kbn-chart-icons/src/assets/chart_treemap.tsx index 587682994102a..f4f8cc3538c89 100644 --- a/packages/kbn-chart-icons/src/assets/chart_treemap.tsx +++ b/packages/kbn-chart-icons/src/assets/chart_treemap.tsx @@ -9,18 +9,10 @@ import React from 'react'; import { EuiIconProps } from '@elastic/eui'; import { colors } from './common_styles'; +import { ChartIconWrapper } from './icon_simple_wrapper'; -export const IconChartTreemap = ({ title, titleId, ...props }: Omit) => ( - - {title ? {title} : null} +export const IconChartTreemap = (props: Omit) => ( + - + ); diff --git a/packages/kbn-chart-icons/src/assets/chart_vertical_bullet.tsx b/packages/kbn-chart-icons/src/assets/chart_vertical_bullet.tsx index db24e31bf25e8..ea2b1370e79aa 100644 --- a/packages/kbn-chart-icons/src/assets/chart_vertical_bullet.tsx +++ b/packages/kbn-chart-icons/src/assets/chart_vertical_bullet.tsx @@ -9,31 +9,17 @@ import React from 'react'; import type { EuiIconProps } from '@elastic/eui'; import { colors } from './common_styles'; +import { ChartIconWrapper } from './icon_simple_wrapper'; -export const IconChartVerticalBullet = ({ - title, - titleId, - ...props -}: Omit) => { - return ( - - {title ? {title} : null} - - - - ); -}; +export const IconChartVerticalBullet = (props: Omit) => ( + + + + +); diff --git a/packages/kbn-chart-icons/src/assets/chart_waffle.tsx b/packages/kbn-chart-icons/src/assets/chart_waffle.tsx index aeeca67a1a511..e47fa2e557816 100644 --- a/packages/kbn-chart-icons/src/assets/chart_waffle.tsx +++ b/packages/kbn-chart-icons/src/assets/chart_waffle.tsx @@ -9,18 +9,10 @@ import React from 'react'; import type { EuiIconProps } from '@elastic/eui'; import { colors } from './common_styles'; +import { ChartIconWrapper } from './icon_simple_wrapper'; -export const IconChartWaffle = ({ title, titleId, ...props }: Omit) => ( - - {title ? : null} +export const IconChartWaffle = (props: Omit) => ( + - + ); diff --git a/packages/kbn-chart-icons/src/assets/donut_hole_icons.tsx b/packages/kbn-chart-icons/src/assets/donut_hole_icons.tsx new file mode 100644 index 0000000000000..137f3992aaa1a --- /dev/null +++ b/packages/kbn-chart-icons/src/assets/donut_hole_icons.tsx @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import type { EuiIconProps } from '@elastic/eui'; +import { IconSimpleWrapper } from './icon_simple_wrapper'; + +export const IconDonutHoleMedium = (props: Omit) => ( + + + + +); + +export const IconDonutHoleLarge = (props: Omit) => ( + + + + +); + +export const IconDonutHoleSmall = (props: Omit) => ( + + + + +); diff --git a/packages/kbn-chart-icons/src/assets/icon_simple_wrapper.tsx b/packages/kbn-chart-icons/src/assets/icon_simple_wrapper.tsx new file mode 100644 index 0000000000000..09a7a1c6129a5 --- /dev/null +++ b/packages/kbn-chart-icons/src/assets/icon_simple_wrapper.tsx @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import type { EuiIconProps } from '@elastic/eui'; + +export const IconSimpleWrapper = ({ + title, + titleId, + children, + height = '16', + width = '16', + ...props +}: Omit) => { + return ( + + {title ? {title} : null} + {children} + + ); +}; + +export const ChartIconWrapper = (props: Omit) => + IconSimpleWrapper({ ...props, width: '30', height: '22' }); diff --git a/packages/kbn-chart-icons/src/assets/index.ts b/packages/kbn-chart-icons/src/assets/index.ts index 0165b2bf0381a..09b85ae816136 100644 --- a/packages/kbn-chart-icons/src/assets/index.ts +++ b/packages/kbn-chart-icons/src/assets/index.ts @@ -38,9 +38,17 @@ export { GlobeIllustration } from './globe_illustration'; export { EuiIconLegend } from './legend'; export { IconRegionMap } from './region_map'; export { IconChartHeatmap } from './chart_heatmap'; +export { IconChartGauge } from './chart_gauge'; export { IconChartHorizontalBullet } from './chart_horizontal_bullet'; export { IconChartVerticalBullet } from './chart_vertical_bullet'; export { IconChartGaugeSemiCircle } from './chart_gauge_semi_circle'; export { IconChartGaugeArc } from './chart_gauge_arc'; export { IconChartGaugeCircle } from './chart_gauge_circle'; +export { IconDonutHoleSmall, IconDonutHoleMedium, IconDonutHoleLarge } from './donut_hole_icons'; +export { + IconChartLinearSimple, + IconChartGaugeSemiCircleSimple, + IconChartGaugeArcSimple, + IconChartGaugeCircleSimple, +} from './chart_gauge_simple_icons'; export { IconChartTagcloud } from './chart_tagcloud'; diff --git a/packages/kbn-chart-icons/src/assets/legend.tsx b/packages/kbn-chart-icons/src/assets/legend.tsx index ce1a8ef9dcbc4..9dc0b09e6cba1 100644 --- a/packages/kbn-chart-icons/src/assets/legend.tsx +++ b/packages/kbn-chart-icons/src/assets/legend.tsx @@ -7,18 +7,11 @@ */ import React from 'react'; +import { EuiIconProps } from '@elastic/eui'; +import { IconSimpleWrapper } from './icon_simple_wrapper'; -export const EuiIconLegend = ({ title, titleId, ...props }: { title: string; titleId: string }) => ( - - {title ? {title} : null} +export const EuiIconLegend = (props: Omit) => ( + - + ); diff --git a/packages/kbn-chart-icons/src/assets/region_map.tsx b/packages/kbn-chart-icons/src/assets/region_map.tsx index 1de9bdaaa2380..5242c0d1c14b8 100644 --- a/packages/kbn-chart-icons/src/assets/region_map.tsx +++ b/packages/kbn-chart-icons/src/assets/region_map.tsx @@ -6,17 +6,13 @@ * Side Public License, v 1. */ -import React, { FunctionComponent } from 'react'; +import React from 'react'; import type { EuiIconProps } from '@elastic/eui'; import { colors } from './common_styles'; +import { ChartIconWrapper } from './icon_simple_wrapper'; -export const IconRegionMap: FunctionComponent = ({ - title, - titleId, - ...props -}: Omit) => ( - - {title ? : null} +export const IconRegionMap = (props: Omit) => ( + - + ); diff --git a/packages/kbn-test-eui-helpers/src/rtl_helpers.tsx b/packages/kbn-test-eui-helpers/src/rtl_helpers.tsx index 58f0444f8b63f..5d7873c66c66d 100644 --- a/packages/kbn-test-eui-helpers/src/rtl_helpers.tsx +++ b/packages/kbn-test-eui-helpers/src/rtl_helpers.tsx @@ -10,12 +10,14 @@ import moment from 'moment'; import userEvent from '@testing-library/user-event'; -import { screen, within, fireEvent } from '@testing-library/react'; - -export const getSelectedButtonInGroup = (testId: string) => () => { - const buttonGroup = screen.getByTestId(testId); - return within(buttonGroup).getByRole('button', { pressed: true }); -}; +import { screen, within, fireEvent, Screen } from '@testing-library/react'; + +export const getSelectedButtonInGroup = + (testId: string, container: Screen | ReturnType = screen) => + () => { + const buttonGroup = container.getByTestId(testId); + return within(buttonGroup).getByRole('button', { pressed: true }); + }; export class EuiButtonGroupTestHarness { #testId: string; diff --git a/src/plugins/unified_histogram/public/__mocks__/suggestions.ts b/src/plugins/unified_histogram/public/__mocks__/suggestions.ts index 9da1fb2fc4317..76bbd409e2bdd 100644 --- a/src/plugins/unified_histogram/public/__mocks__/suggestions.ts +++ b/src/plugins/unified_histogram/public/__mocks__/suggestions.ts @@ -134,7 +134,7 @@ export const currentSuggestionMock = { } as Suggestion; export const histogramESQLSuggestionMock = { - title: 'Bar vertical stacked', + title: 'Bar', score: 0.16666666666666666, hide: false, incomplete: false, diff --git a/test/functional/apps/discover/group3/_lens_vis.ts b/test/functional/apps/discover/group3/_lens_vis.ts index cc48864976811..de9220e318273 100644 --- a/test/functional/apps/discover/group3/_lens_vis.ts +++ b/test/functional/apps/discover/group3/_lens_vis.ts @@ -275,7 +275,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.discover.waitUntilSearchingHasFinished(); - expect(await getCurrentVisTitle()).to.be('Bar vertical stacked'); + expect(await getCurrentVisTitle()).to.be('Bar'); await checkESQLHistogramVis(defaultTimespanESQL, '100'); @@ -305,12 +305,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.discover.waitUntilSearchingHasFinished(); await checkESQLHistogramVis(defaultTimespanESQL, '5'); - await PageObjects.discover.chooseLensSuggestion('donut'); + await PageObjects.discover.chooseLensSuggestion('pie'); await testSubjects.existOrFail('unsavedChangesBadge'); expect(await monacoEditor.getCodeEditorValue()).to.contain('averageA'); - expect(await getCurrentVisTitle()).to.be('Donut'); + expect(await getCurrentVisTitle()).to.be('Pie'); await testSubjects.existOrFail('partitionVisChart'); expect(await PageObjects.discover.getVisContextSuggestionType()).to.be('lensSuggestion'); @@ -354,12 +354,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.discover.waitUntilSearchingHasFinished(); await checkESQLHistogramVis(defaultTimespanESQL, '5'); - await PageObjects.discover.chooseLensSuggestion('donut'); + await PageObjects.discover.chooseLensSuggestion('pie'); await testSubjects.existOrFail('unsavedChangesBadge'); expect(await monacoEditor.getCodeEditorValue()).to.contain('averageA'); - expect(await getCurrentVisTitle()).to.be('Donut'); + expect(await getCurrentVisTitle()).to.be('Pie'); await testSubjects.existOrFail('partitionVisChart'); expect(await PageObjects.discover.getVisContextSuggestionType()).to.be('lensSuggestion'); @@ -369,7 +369,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.discover.waitUntilSearchingHasFinished(); await testSubjects.missingOrFail('unsavedChangesBadge'); - expect(await getCurrentVisTitle()).to.be('Donut'); + expect(await getCurrentVisTitle()).to.be('Pie'); await testSubjects.existOrFail('partitionVisChart'); expect(await PageObjects.discover.getVisContextSuggestionType()).to.be('lensSuggestion'); @@ -378,7 +378,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.discover.waitUntilSearchingHasFinished(); await testSubjects.missingOrFail('unsavedChangesBadge'); - expect(await getCurrentVisTitle()).to.be('Donut'); + expect(await getCurrentVisTitle()).to.be('Pie'); await testSubjects.existOrFail('partitionVisChart'); }); @@ -407,12 +407,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.discover.waitUntilSearchingHasFinished(); await checkESQLHistogramVis(defaultTimespanESQL, '5'); - await PageObjects.discover.chooseLensSuggestion('donut'); + await PageObjects.discover.chooseLensSuggestion('pie'); await testSubjects.existOrFail('unsavedChangesBadge'); expect(await monacoEditor.getCodeEditorValue()).to.contain('averageA'); - expect(await getCurrentVisTitle()).to.be('Donut'); + expect(await getCurrentVisTitle()).to.be('Pie'); await testSubjects.existOrFail('partitionVisChart'); // now we customize the vis again @@ -454,29 +454,29 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.discover.waitUntilSearchingHasFinished(); await checkESQLHistogramVis(defaultTimespanESQL, '5'); - await PageObjects.discover.chooseLensSuggestion('donut'); + await PageObjects.discover.chooseLensSuggestion('pie'); await PageObjects.discover.saveSearch('testCustomESQLVis'); - await PageObjects.discover.saveSearch('testCustomESQLVisDonut', true); + await PageObjects.discover.saveSearch('testCustomESQLVisPartition', true); - expect(await getCurrentVisTitle()).to.be('Donut'); + expect(await getCurrentVisTitle()).to.be('Pie'); await testSubjects.existOrFail('partitionVisChart'); await browser.refresh(); await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.discover.waitUntilSearchingHasFinished(); - expect(await getCurrentVisTitle()).to.be('Donut'); + expect(await getCurrentVisTitle()).to.be('Pie'); await testSubjects.existOrFail('partitionVisChart'); }); it('should be able to load a saved search with custom vis, edit query and revert changes', async () => { - await PageObjects.discover.loadSavedSearch('testCustomESQLVisDonut'); + await PageObjects.discover.loadSavedSearch('testCustomESQLVisPartition'); await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.discover.waitUntilSearchingHasFinished(); - expect(await getCurrentVisTitle()).to.be('Donut'); + expect(await getCurrentVisTitle()).to.be('Pie'); await testSubjects.existOrFail('partitionVisChart'); expect(await PageObjects.discover.getVisContextSuggestionType()).to.be('lensSuggestion'); @@ -489,7 +489,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.discover.waitUntilSearchingHasFinished(); - expect(await getCurrentVisTitle()).to.be('Bar vertical stacked'); + expect(await getCurrentVisTitle()).to.be('Bar'); expect(await PageObjects.discover.getVisContextSuggestionType()).to.be('histogramForESQL'); await checkESQLHistogramVis(defaultTimespanESQL, '100'); @@ -503,49 +503,49 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.discover.waitUntilSearchingHasFinished(); await testSubjects.missingOrFail('unsavedChangesBadge'); - expect(await getCurrentVisTitle()).to.be('Donut'); + expect(await getCurrentVisTitle()).to.be('Pie'); await testSubjects.existOrFail('partitionVisChart'); expect(await PageObjects.discover.getVisContextSuggestionType()).to.be('lensSuggestion'); expect(await monacoEditor.getCodeEditorValue()).to.contain('averageB'); - // should be still Donut after reverting and saving again - await PageObjects.discover.saveSearch('testCustomESQLVisDonut'); + // should be still Pie after reverting and saving again + await PageObjects.discover.saveSearch('testCustomESQLVisPartition'); await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.discover.waitUntilSearchingHasFinished(); await testSubjects.missingOrFail('unsavedChangesBadge'); - expect(await getCurrentVisTitle()).to.be('Donut'); + expect(await getCurrentVisTitle()).to.be('Pie'); await testSubjects.existOrFail('partitionVisChart'); expect(await PageObjects.discover.getVisContextSuggestionType()).to.be('lensSuggestion'); }); it('should be able to change to an unfamiliar vis type via lens flyout', async () => { - await PageObjects.discover.loadSavedSearch('testCustomESQLVisDonut'); + await PageObjects.discover.loadSavedSearch('testCustomESQLVisPartition'); await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.discover.waitUntilSearchingHasFinished(); - expect(await getCurrentVisTitle()).to.be('Donut'); + expect(await getCurrentVisTitle()).to.be('Pie'); await testSubjects.existOrFail('partitionVisChart'); await testSubjects.missingOrFail('unsavedChangesBadge'); - await changeVisShape('Pie'); + await changeVisShape('Treemap'); await testSubjects.existOrFail('unsavedChangesBadge'); - expect(await getCurrentVisTitle()).to.be('Pie'); + expect(await getCurrentVisTitle()).to.be('Treemap'); await testSubjects.existOrFail('partitionVisChart'); expect(await PageObjects.discover.getVisContextSuggestionType()).to.be('lensSuggestion'); - await PageObjects.discover.saveSearch('testCustomESQLVisPie', true); + await PageObjects.discover.saveSearch('testCustomESQLVisTreemap', true); await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.discover.waitUntilSearchingHasFinished(); - expect(await getCurrentVisTitle()).to.be('Pie'); + expect(await getCurrentVisTitle()).to.be('Treemap'); await testSubjects.existOrFail('partitionVisChart'); await browser.refresh(); @@ -553,7 +553,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.discover.waitUntilSearchingHasFinished(); - expect(await getCurrentVisTitle()).to.be('Pie'); + expect(await getCurrentVisTitle()).to.be('Treemap'); await testSubjects.existOrFail('partitionVisChart'); expect(await PageObjects.discover.getVisContextSuggestionType()).to.be('lensSuggestion'); @@ -564,7 +564,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.discover.waitUntilSearchingHasFinished(); - expect(await getCurrentVisTitle()).to.be('Bar vertical stacked'); + expect(await getCurrentVisTitle()).to.be('Bar'); expect(await PageObjects.discover.getVisContextSuggestionType()).to.be('lensSuggestion'); await testSubjects.existOrFail('unsavedChangesBadge'); @@ -575,7 +575,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.discover.waitUntilSearchingHasFinished(); await testSubjects.missingOrFail('unsavedChangesBadge'); - expect(await getCurrentVisTitle()).to.be('Pie'); + expect(await getCurrentVisTitle()).to.be('Treemap'); await testSubjects.existOrFail('partitionVisChart'); }); @@ -585,7 +585,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.discover.waitUntilSearchingHasFinished(); - expect(await getCurrentVisTitle()).to.be('Donut'); + expect(await getCurrentVisTitle()).to.be('Pie'); await testSubjects.existOrFail('partitionVisChart'); await testSubjects.missingOrFail('unsavedChangesBadge'); @@ -602,7 +602,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.discover.waitUntilSearchingHasFinished(); await testSubjects.missingOrFail('unsavedChangesBadge'); - expect(await getCurrentVisTitle()).to.be('Donut'); + expect(await getCurrentVisTitle()).to.be('Pie'); await testSubjects.existOrFail('partitionVisChart'); await PageObjects.discover.chooseLensSuggestion('barVerticalStacked'); @@ -629,7 +629,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.discover.waitUntilSearchingHasFinished(); - expect(await getCurrentVisTitle()).to.be('Bar vertical stacked'); + expect(await getCurrentVisTitle()).to.be('Bar'); expect(await PageObjects.discover.getVisContextSuggestionType()).to.be('lensSuggestion'); await testSubjects.missingOrFail('unsavedChangesBadge'); @@ -644,8 +644,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.discover.waitUntilSearchingHasFinished(); await testSubjects.missingOrFail('unsavedChangesBadge'); - await PageObjects.discover.chooseLensSuggestion('donut'); - expect(await getCurrentVisTitle()).to.be('Donut'); + await PageObjects.discover.chooseLensSuggestion('pie'); + expect(await getCurrentVisTitle()).to.be('Pie'); await testSubjects.existOrFail('partitionVisChart'); expect(await PageObjects.discover.getVisContextSuggestionType()).to.be('lensSuggestion'); diff --git a/x-pack/examples/third_party_vis_lens_example/public/visualization.tsx b/x-pack/examples/third_party_vis_lens_example/public/visualization.tsx index 60b45bad9c423..36a335eac7aba 100644 --- a/x-pack/examples/third_party_vis_lens_example/public/visualization.tsx +++ b/x-pack/examples/third_party_vis_lens_example/public/visualization.tsx @@ -44,20 +44,19 @@ export const getRotatingNumberVisualization = ({ }): Visualization => ({ id: 'rotatingNumber', + getVisualizationTypeId() { + return 'rotatingNumber'; + }, visualizationTypes: [ { id: 'rotatingNumber', icon: 'refresh', label: 'Rotating number', - groupLabel: 'Goal and single value', + description: 'A number that rotates', sortPriority: 3, }, ], - getVisualizationTypeId() { - return 'rotatingNumber'; - }, - clearLayer(state) { return { ...state, diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_option.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_option.tsx new file mode 100644 index 0000000000000..13dd822ece20e --- /dev/null +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_option.tsx @@ -0,0 +1,115 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import './chart_switch.scss'; +import React from 'react'; +import { + EuiFlexItem, + EuiFlexGroup, + EuiIcon, + EuiText, + EuiHighlight, + IconType, + useEuiTheme, + EuiIconTip, +} from '@elastic/eui'; +import { css } from '@emotion/react'; +import { i18n } from '@kbn/i18n'; + +export const ChartOption = ({ + option, + searchValue = '', +}: { + option: { label: string; description?: string; icon?: IconType }; + searchValue?: string; +}) => { + return ( + + + + + + + {option.label} + + + {option.description ? ( + {option.description} + ) : null} + + + + ); +}; + +export const getDataLossWarning = (dataLoss: 'nothing' | 'layers' | 'everything' | 'columns') => { + if (dataLoss === 'nothing') { + return; + } + if (dataLoss === 'everything') { + return i18n.translate('xpack.lens.chartSwitch.dataLossEverything', { + defaultMessage: 'Changing to this visualization clears the current configuration.', + }); + } + if (dataLoss === 'layers') { + return i18n.translate('xpack.lens.chartSwitch.dataLossLayersDescription', { + defaultMessage: + 'Changing to this visualization modifies currently selected layer`s configuration and removes all other layers.', + }); + } else + return i18n.translate('xpack.lens.chartSwitch.dataLossColumns', { + defaultMessage: `Changing to this visualization modifies the current configuration.`, + }); +}; + +const CheckIcon = () => { + const { euiTheme } = useEuiTheme(); + return ; +}; + +const DataLossWarning = ({ content, id }: { content?: string; id: string }) => { + const { euiTheme } = useEuiTheme(); + if (!content) return null; + return ( + + ); +}; + +export const ChartSwitchOptionPrepend = ({ + isChecked, + dataLoss, + subtypeId, +}: { + isChecked: boolean; + dataLoss: 'nothing' | 'layers' | 'everything' | 'columns'; + subtypeId: string; +}) => { + const dataLossWarning = getDataLossWarning(dataLoss); + return ( + + {isChecked && } + {dataLossWarning && } + {!dataLossWarning && !isChecked && } + + ); +}; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_option_append.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_option_append.tsx deleted file mode 100644 index 52d84054ebdb4..0000000000000 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_option_append.tsx +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import './chart_switch.scss'; -import React from 'react'; -import { EuiFlexItem, EuiIconTip, EuiFlexGroup } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { ExperimentalBadge } from '../../../../shared_components'; - -export const getDataLossWarning = (dataLoss: 'nothing' | 'layers' | 'everything' | 'columns') => { - if (dataLoss === 'nothing') { - return; - } - if (dataLoss === 'everything') { - return i18n.translate('xpack.lens.chartSwitch.dataLossEverything', { - defaultMessage: 'Changing to this visualization clears the current configuration.', - }); - } - if (dataLoss === 'layers') { - return i18n.translate('xpack.lens.chartSwitch.dataLossLayersDescription', { - defaultMessage: - 'Changing to this visualization modifies currently selected layer`s configuration and removes all other layers.', - }); - } else - return i18n.translate('xpack.lens.chartSwitch.dataLossColumns', { - defaultMessage: `Changing to this visualization modifies the current configuration.`, - }); -}; - -const DataLossWarning = ({ content, id }: { content?: string; id: string }) => { - if (!content) return null; - return ( - - - - ); -}; - -export const ChartOptionAppend = ({ - dataLoss, - showExperimentalBadge, - id, -}: { - dataLoss: 'nothing' | 'layers' | 'everything' | 'columns'; - showExperimentalBadge?: boolean; - id: string; -}) => ( - - {showExperimentalBadge ? ( - - - - ) : null} - - -); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_switch.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_switch.scss index 9cd11cf1396db..1a33ed18581f4 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_switch.scss +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_switch.scss @@ -5,6 +5,10 @@ } } +.lnsChartSwitch__options { + width: 384px; +} + .lnsChartSwitch__summaryIcon { transform: translateY(-1px); color: $euiTextSubduedColor; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_switch.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_switch.test.tsx index f2c3440850a9e..ef10e90a26ddf 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_switch.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_switch.test.tsx @@ -18,8 +18,10 @@ import { } from '../../../../mocks'; import { DatasourcePublicAPI, SuggestionRequest, DatasourceSuggestion } from '../../../../types'; -import { ChartSwitch, ChartSwitchProps } from './chart_switch'; +import { ChartSwitchProps } from './chart_switch'; +import { ChartSwitchPopover } from './chart_switch_popover'; import { LensAppState, applyChanges } from '../../../../state_management'; +import faker from 'faker'; const mockFrame = (layers: string[]) => ({ ...createMockFramePublicAPI(), @@ -189,7 +191,8 @@ describe('chart_switch', () => { icon: 'empty', id, label: id, - groupLabel: `${id}Group`, + sortPriority: 1, + description: faker.lorem.sentence(), })), getVisualizationTypeId: jest.fn((state) => state.type), getSuggestions: jest.fn((options) => { @@ -224,7 +227,7 @@ describe('chart_switch', () => { } ) => { const { store, ...rtlRender } = renderWithReduxStore( - void; } -type SelectableEntry = EuiSelectableOption<{ value: string }>; - -const MAX_LIST_HEIGHT = 380; -const ENTRY_HEIGHT = 32; +const sortByPriority = (a: VisualizationType, b: VisualizationType) => { + return a.sortPriority - b.sortPriority; +}; -function computeListHeight(list: SelectableEntry[], maxHeight: number): number { - if (list.length === 0) { - return 0; - } - return Math.min(list.length * ENTRY_HEIGHT, maxHeight); -} +const deprecatedGroupChartSwitchElement = { + key: 'deprecated', + value: 'deprecated', + label: i18n.translate('xpack.lens.configPanel.deprecated', { + defaultMessage: 'Deprecated', + }), + isGroupLabel: true, + 'aria-label': 'deprecated', + 'data-test-subj': `lnsChartSwitchPopover_deprecated`, +}; function safeFnCall(action: () => TReturn, defaultReturnValue: TReturn): TReturn { try { @@ -82,28 +83,20 @@ function safeFnCall(action: () => TReturn, defaultReturnValue: TReturn) } } -function getCurrentVisualizationId( - activeVisualization: Visualization, - visualizationState: unknown -) { - return safeFnCall( - () => activeVisualization.getVisualizationTypeId(visualizationState), - undefined - ); -} - export const ChartSwitch = memo(function ChartSwitch({ framePublicAPI, visualizationMap, datasourceMap, layerId, + onChartSelect, }: ChartSwitchProps) { - const [flyoutOpen, setFlyoutOpen] = useState(false); const dispatchLens = useLensDispatch(); const activeDatasourceId = useLensSelector(selectActiveDatasourceId); const visualization = useLensSelector(selectVisualization); const datasourceStates = useLensSelector(selectDatasourceStates); + const [searchTerm, setSearchTerm] = useState(''); + const commitSelection = (selection: VisualizationSelection) => { switchToSuggestion( dispatchLens, @@ -142,9 +135,6 @@ export const ChartSwitch = memo(function ChartSwitch({ return state; }; const layers = Object.entries(framePublicAPI.datasourceLayers); - const containsData = layers.some( - ([_layerId, datasource]) => datasource && datasource.getTableSpec().length > 0 - ); // Always show the active visualization as a valid selection if ( @@ -183,19 +173,7 @@ export const ChartSwitch = memo(function ChartSwitch({ layerId ); - let dataLoss: VisualizationSelection['dataLoss']; - - if (!containsData) { - dataLoss = 'nothing'; - } else if (!topSuggestion) { - dataLoss = 'everything'; - } else if (layers.length > 1 && layers.length > topSuggestion.keptLayerIds.length) { - dataLoss = 'layers'; - } else if (topSuggestion.columns !== layers[0][1]?.getTableSpec().length) { - dataLoss = 'columns'; - } else { - dataLoss = 'nothing'; - } + const dataLoss = getDataLoss(layers, topSuggestion); function addNewLayer() { const newLayerId = generateId(); @@ -238,31 +216,18 @@ export const ChartSwitch = memo(function ChartSwitch({ }; } - const [searchTerm, setSearchTerm] = useState(''); - const { visualizationTypes, visualizationsLookup } = useMemo( () => { - if (!flyoutOpen) { - return { visualizationTypes: [], visualizationsLookup: {} }; - } const subVisualizationId = visualization.activeId && visualizationMap[visualization.activeId] - ? getCurrentVisualizationId(visualizationMap[visualization.activeId], visualization.state) + ? getVisualizationTypeId( + visualizationMap[visualization.activeId], + visualization.state, + layerId + ) : undefined; const lowercasedSearchTerm = searchTerm.toLowerCase(); - // reorganize visualizations in groups - const grouped: Record< - string, - { - priority: number; - visualizations: Array< - VisualizationType & { - visualizationId: string; - selection: VisualizationSelection; - } - >; - } - > = {}; + // Will need it later on to quickly pick up the metadata from it const lookup: Record< string, @@ -271,176 +236,131 @@ export const ChartSwitch = memo(function ChartSwitch({ selection: VisualizationSelection; } > = {}; + + const chartSwitchPositions: VisChartSwitchPosition[] = []; + const deprecatedChartSwitchPositions: VisChartSwitchPosition[] = []; Object.entries(visualizationMap).forEach(([visualizationId, v]) => { - for (const visualizationType of v.visualizationTypes) { + const chartSwitchOptions = v.visualizationTypes; + for (const visualizationType of chartSwitchOptions) { const isSearchMatch = visualizationType.label.toLowerCase().includes(lowercasedSearchTerm) || - visualizationType.fullLabel?.toLowerCase().includes(lowercasedSearchTerm); + visualizationType.description?.toLowerCase().includes(lowercasedSearchTerm); if (isSearchMatch) { - grouped[visualizationType.groupLabel] = grouped[visualizationType.groupLabel] || { - priority: 0, - visualizations: [], - }; + const subtypeId = findSubtypeId(visualizationType, subVisualizationId); + const visualizationEntry = { ...visualizationType, visualizationId, - selection: getSelection(visualizationId, visualizationType.id), + selection: getSelection(visualizationId, subtypeId), }; - grouped[visualizationType.groupLabel].priority += visualizationType.sortPriority || 0; - grouped[visualizationType.groupLabel].visualizations.push(visualizationEntry); + if (visualizationEntry.isDeprecated) { + deprecatedChartSwitchPositions.push(visualizationEntry); + } else { + chartSwitchPositions.push(visualizationEntry); + } lookup[`${visualizationId}:${visualizationType.id}`] = visualizationEntry; } } }); + const toSelectableEntry = (v: VisChartSwitchPosition): SelectableEntry => { + const isChecked = subVisualizationId === v.id; + return { + 'aria-label': v.label, + className: 'lnsChartSwitch__option', + key: `${v.visualizationId}:${v.id}`, // todo: should we simplify? + value: `${v.visualizationId}:${v.id}`, + 'data-test-subj': `lnsChartSwitchPopover_${v.id}`, + label: v.label, + prepend: ( + + ), + data: { + description: v.description, + icon: v.icon, + }, + append: v.showExperimentalBadge ? : null, + // Apparently checked: null is not valid for TS + ...(isChecked && { checked: 'on' }), + }; + }; + return { - visualizationTypes: Object.keys(grouped) - .sort((groupA, groupB) => { - return grouped[groupB].priority - grouped[groupA].priority; - }) - .flatMap((group): SelectableEntry[] => { - const { visualizations } = grouped[group]; - if (visualizations.length === 0) { - return []; - } - return [ - { - key: group, - label: group, - isGroupLabel: true, - 'aria-label': group, - 'data-test-subj': `lnsChartSwitchPopover_${group}`, - } as SelectableEntry, - ].concat( - visualizations - // alphabetical order within each group - .sort((a, b) => { - return ( - (b.sortOrder ?? 0) - (a.sortOrder ?? 0) || - (a.fullLabel || a.label).localeCompare(b.fullLabel || b.label) - ); - }) - .map((v): SelectableEntry => { - return { - 'aria-label': v.fullLabel || v.label, - className: 'lnsChartSwitch__option', - isGroupLabel: false, - key: `${v.visualizationId}:${v.id}`, - value: `${v.visualizationId}:${v.id}`, - 'data-test-subj': `lnsChartSwitchPopover_${v.id}`, - label: v.fullLabel || v.label, - prepend: ( - - ), - append: - v.selection.dataLoss !== 'nothing' || v.showExperimentalBadge ? ( - - ) : null, - // Apparently checked: null is not valid for TS - ...(subVisualizationId === v.id && { checked: 'on' }), - }; - }) - ); - }), + visualizationTypes: chartSwitchPositions + .sort(sortByPriority) + .map(toSelectableEntry) + .concat( + deprecatedChartSwitchPositions.length + ? [ + deprecatedGroupChartSwitchElement, + ...deprecatedChartSwitchPositions.map(toSelectableEntry), + ] + : [] + ), visualizationsLookup: lookup, }; }, // eslint-disable-next-line react-hooks/exhaustive-deps - [ - flyoutOpen, - visualizationMap, - framePublicAPI, - visualization.activeId, - visualization.state, - searchTerm, - ] + [visualizationMap, framePublicAPI, visualization.activeId, visualization.state, searchTerm] ); - const { icon, label } = (visualization.activeId && - visualizationMap[visualization.activeId]?.getDescription(visualization.state, layerId)) || { - label: i18n.translate('xpack.lens.configPanel.selectVisualization', { - defaultMessage: 'Select a visualization', - }), - icon: undefined, - }; - return ( -
- setFlyoutOpen(!flyoutOpen)} - /> + ) => { + onChartSelect(); + const chosenType = newOptions.find(({ checked }) => checked === 'on'); + if (!chosenType || !chosenType.value) { + return; } - isOpen={flyoutOpen} - closePopover={() => setFlyoutOpen(false)} - anchorPosition="downLeft" - > - - - - {i18n.translate('xpack.lens.configPanel.visualizationType', { - defaultMessage: 'Visualization type', - })} - - - - setSearchTerm(value), - }} - options={visualizationTypes} - onChange={(newOptions) => { - setFlyoutOpen(false); - const chosenType = newOptions.find(({ checked }) => checked === 'on'); - if (!chosenType) { - return; - } - const id = chosenType.value!; - commitSelection(visualizationsLookup[id].selection); - }} - noMatchesMessage={ - {searchTerm}, - }} - /> - } - > - {(list, search) => ( - <> - {search} - {list} - - )} - - -
+ const id = chosenType.value; + commitSelection(visualizationsLookup[id].selection); + }} + searchTerm={searchTerm} + setSearchTerm={setSearchTerm} + /> ); }); +const getVisualizationSubtypes = ( + visualization: Visualization, + state: unknown, + layerId?: string +) => { + const typeId = safeFnCall(() => visualization.getVisualizationTypeId(state, layerId), undefined); + const type = visualization.visualizationTypes.find((t) => t.id === typeId); + if (type?.subtypes) { + return type.subtypes; + } + return [typeId]; +}; + +const getVisualizationTypeId = ( + activeVisualization: Visualization, + visualizationState: unknown, + layerId?: string +) => + safeFnCall( + () => activeVisualization.getVisualizationTypeId(visualizationState, layerId), + undefined + ); + +const findSubtypeId = (visType: VisualizationType, subtypeId?: string) => { + if (visType.subtypes) { + return ( + visType.subtypes.find((subtype) => subtype === subtypeId) || + visType.getCompatibleSubtype?.(subtypeId) || + visType.subtypes[0] + ); + } + return visType.id; +}; + function getTopSuggestion( visualizationMap: VisualizationMap, datasourceMap: DatasourceMap, @@ -475,15 +395,9 @@ function getTopSuggestion( const suggestions = unfilteredSuggestions.filter((suggestion) => { // don't use extended versions of current data table on switching between visualizations // to avoid confusing the user. - return ( - suggestion.changeType !== 'extended' && - safeFnCall( - () => - newVisualization.getVisualizationTypeId(suggestion.visualizationState) === - subVisualizationId, - false - ) - ); + + const subtypes = getVisualizationSubtypes(newVisualization, suggestion.visualizationState); + return suggestion.changeType !== 'extended' && subtypes.includes(subVisualizationId); }); return ( @@ -492,3 +406,23 @@ function getTopSuggestion( suggestions[0] ); } + +const getDataLoss = ( + layers: Array<[string, DatasourcePublicAPI | undefined]>, + topSuggestion: Suggestion | undefined +) => { + const containsData = layers.some( + ([_layerId, datasource]) => datasource && datasource.getTableSpec().length > 0 + ); + if (!containsData) { + return 'nothing'; + } else if (!topSuggestion) { + return 'everything'; + } else if (layers.length > 1 && layers.length > topSuggestion.keptLayerIds.length) { + return 'layers'; + } else if (topSuggestion.columns !== layers[0][1]?.getTableSpec().length) { + return 'columns'; + } else { + return 'nothing'; + } +}; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_switch_popover.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_switch_popover.tsx new file mode 100644 index 0000000000000..bc2fdfbf35813 --- /dev/null +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_switch_popover.tsx @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import './chart_switch.scss'; +import React, { useState, memo } from 'react'; +import { EuiPopover } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { ChartSwitchTrigger } from '@kbn/visualization-ui-components'; +import { useLensSelector, selectVisualization } from '../../../../state_management'; +import { ChartSwitch, ChartSwitchProps } from './chart_switch'; + +export const ChartSwitchPopover = memo(function ChartSwitchPopover( + props: Omit +) { + const [flyoutOpen, setFlyoutOpen] = useState(false); + const visualization = useLensSelector(selectVisualization); + + const { icon, label } = (visualization.activeId && + props.visualizationMap[visualization.activeId]?.getDescription( + visualization.state, + props.layerId + )) || { + label: i18n.translate('xpack.lens.configPanel.selectVisualization', { + defaultMessage: 'Select a visualization', + }), + icon: undefined, + }; + + return ( +
+ setFlyoutOpen(!flyoutOpen)} + /> + } + isOpen={flyoutOpen} + closePopover={() => setFlyoutOpen(false)} + anchorPosition="downLeft" + > + {flyoutOpen ? setFlyoutOpen(false)} /> : null} + +
+ ); +}); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_switch_selectable.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_switch_selectable.tsx new file mode 100644 index 0000000000000..8fa8a7a8a8a44 --- /dev/null +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch/chart_switch_selectable.tsx @@ -0,0 +1,92 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { + EuiSelectable, + EuiPopoverTitle, + EuiSelectableOption, + EuiSelectableProps, + IconType, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { css } from '@emotion/react'; +import { ChartOption } from './chart_option'; + +export type SelectableEntry = EuiSelectableOption<{ + value: string; + description?: string; + icon?: IconType; +}>; + +const ITEM_HEIGHT = 52; +const MAX_ITEMS_COUNT = 6; +const MAX_LIST_HEIGHT = ITEM_HEIGHT * MAX_ITEMS_COUNT; + +function computeListHeight(list: SelectableEntry[]) { + if (list.length > MAX_ITEMS_COUNT) { + return MAX_LIST_HEIGHT; + } +} + +export const ChartSwitchSelectable = ({ + setSearchTerm, + searchTerm, + ...props +}: { setSearchTerm: (val: string) => void; searchTerm: string } & EuiSelectableProps) => { + return ( + ( + + )} + noMatchesMessage={ +
+ {searchTerm}, + }} + /> +
+ } + {...props} + searchable + > + {(list, search) => ( + <> + {search} + {list} + + )} +
+ ); +}; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.scss index 7b74d0e966410..5fab178a9d1f5 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.scss +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.scss @@ -1,12 +1,17 @@ -.lnsLayerAddButton:hover { - text-decoration: none; - .lnsLayerAddButton__label { - text-decoration: underline; +.lnsLayerAddButton { + &:last-child { + align-self: unset; } + &:hover { + text-decoration: none; - .lnsLayerAddButton__techBadge, - .lnsLayerAddButton__techBadge * { - cursor: pointer; - } + .lnsLayerAddButton__label { + text-decoration: underline; + } + + .lnsLayerAddButton__techBadge, + .lnsLayerAddButton__techBadge * { + cursor: pointer; + }} } diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/layer_actions.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/layer_actions.tsx index 0aaf2b01a462c..2ab2c8e78890c 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/layer_actions.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/layer_actions.tsx @@ -110,7 +110,7 @@ const InContextMenuActions = (props: LayerActionsProps) => { display="empty" color="text" size="s" - iconType="boxesHorizontal" + iconType="boxesVertical" aria-label={i18n.translate('xpack.lens.layer.actions.contextMenuAriaLabel', { defaultMessage: `Layer actions`, })} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_header.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_header.test.tsx index fd983ee36a15b..173f69e2e21c1 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_header.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_header.test.tsx @@ -43,7 +43,8 @@ describe('LayerHeader', () => { icon: 'empty', id, label: faker.lorem.word(), - groupLabel: `${id}Group`, + description: faker.lorem.sentence(), + sortPriority: 1, })), }, hiddenVis: { ...createMockVisualization('hiddenVis'), hideFromChartSwitch: () => true }, @@ -83,7 +84,9 @@ describe('LayerHeader', () => { return screen.queryByRole('presentation', { name: label }); }; const getAllChartSwitchOptions = () => { - return screen.queryAllByRole('presentation').map((el) => el.textContent); + return screen + .queryAllByRole('option') + .map((el) => (el as HTMLInputElement).getAttribute('value')); }; return { ...rtlRender, @@ -105,11 +108,11 @@ describe('LayerHeader', () => { openChartSwitch(); expect(queryChartOptionByLabel('hiddenVis')).not.toBeInTheDocument(); expect(getAllChartSwitchOptions()).toEqual([ - 'testVisGroup', - 'testVis2Group', - 'subvisC1Group', - 'subvisC2Group', - 'subvisC3Group', + 'testVis:testVis', + 'testVis2:testVis2', + 'testVis3:subvisC1', + 'testVis3:subvisC2', + 'testVis3:subvisC3', ]); }); @@ -138,6 +141,10 @@ describe('LayerHeader', () => { activeVisualizationId: 'testVis3', }); openChartSwitch(); - expect(getAllChartSwitchOptions()).toEqual(['subvisC1Group', 'subvisC2Group', 'subvisC3Group']); + expect(getAllChartSwitchOptions()).toEqual([ + 'testVis3:subvisC1', + 'testVis3:subvisC2', + 'testVis3:subvisC3', + ]); }); }); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_header.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_header.tsx index 1313c107555f0..1c783df0a9ecd 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_header.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_header.tsx @@ -6,6 +6,7 @@ */ import React from 'react'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { StaticHeader } from '../../../shared_components'; import { DatasourceMap, @@ -13,7 +14,7 @@ import { VisualizationLayerWidgetProps, VisualizationMap, } from '../../../types'; -import { ChartSwitch } from './chart_switch'; +import { ChartSwitchPopover } from './chart_switch/chart_switch_popover'; export function LayerHeader({ activeVisualizationId, @@ -53,8 +54,24 @@ export function LayerHeader({ return ; } - return ( - + + + + + + + + ) : ( + ; @@ -29,7 +29,7 @@ export const lensPluginMock = { navigateToPrefilledEditor: jest.fn(), getXyVisTypes: jest .fn() - .mockReturnValue(new Promise((resolve) => resolve(visualizationTypes))), + .mockReturnValue(new Promise((resolve) => resolve(visualizationSubtypes))), stateHelperApi: jest.fn().mockResolvedValue({ formula: createFormulaPublicApi(), diff --git a/x-pack/plugins/lens/public/mocks/visualization_mock.tsx b/x-pack/plugins/lens/public/mocks/visualization_mock.tsx index e9d32314c4706..f0513e66de0c5 100644 --- a/x-pack/plugins/lens/public/mocks/visualization_mock.tsx +++ b/x-pack/plugins/lens/public/mocks/visualization_mock.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { LayerTypes } from '@kbn/expression-xy-plugin/public'; import { toExpression } from '@kbn/interpreter'; +import faker from 'faker'; import { Visualization, VisualizationMap } from '../types'; export function createMockVisualization( @@ -25,7 +26,8 @@ export function createMockVisualization( icon: 'empty', id, label: id, - groupLabel: `${id}Group`, + sortPriority: 1, + description: faker.lorem.sentence(), }, ], appendLayer: jest.fn(), diff --git a/x-pack/plugins/lens/public/plugin.ts b/x-pack/plugins/lens/public/plugin.ts index df37dd9707b04..80dfa0f0398bc 100644 --- a/x-pack/plugins/lens/public/plugin.ts +++ b/x-pack/plugins/lens/public/plugin.ts @@ -699,8 +699,8 @@ export class LensPlugin { return Boolean(core.application.capabilities.visualize?.show); }, getXyVisTypes: async () => { - const { visualizationTypes } = await import('./visualizations/xy/types'); - return visualizationTypes; + const { visualizationSubtypes } = await import('./visualizations/xy/types'); + return visualizationSubtypes; }, stateHelperApi: async () => { diff --git a/x-pack/plugins/lens/public/shared_components/bar_orientation.tsx b/x-pack/plugins/lens/public/shared_components/bar_orientation.tsx new file mode 100644 index 0000000000000..b9489620c1936 --- /dev/null +++ b/x-pack/plugins/lens/public/shared_components/bar_orientation.tsx @@ -0,0 +1,85 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiButtonGroup, EuiFormRow } from '@elastic/eui'; + +type BarOrientation = 'vertical' | 'horizontal'; + +const getBarOrientationOptions = ( + isDisabled?: boolean +): Array<{ + id: string; + value: BarOrientation; + label: string; + 'data-test-subj': string; + toolTipContent?: string; +}> => [ + { + id: `bar_orientation_horizontal`, + value: 'horizontal', + label: i18n.translate('xpack.lens.shared.barOrientation.horizontal', { + defaultMessage: 'Horizontal', + }), + 'data-test-subj': 'lns_barOrientation_horizontal', + toolTipContent: isDisabled + ? i18n.translate('xpack.lens.shared.barOrientation.disabled', { + defaultMessage: + 'A horizontal bar orientation cannot be applied when there are one or more area visualization layers.', + }) + : undefined, + }, + { + id: `bar_orientation_vertical`, + value: 'vertical', + label: i18n.translate('xpack.lens.shared.barOrientation.vertical', { + defaultMessage: 'Vertical', + }), + 'data-test-subj': 'lns_barOrientation_vertical', + }, +]; + +export interface BarOrientationProps { + barOrientation?: BarOrientation; + onBarOrientationChange: (newMode: BarOrientation) => void; + isDisabled?: boolean; +} + +export const BarOrientationSettings: FC = ({ + barOrientation = 'horizontal', + onBarOrientationChange, + isDisabled = false, +}) => { + const barOrientationOptions = getBarOrientationOptions(isDisabled); + const label = i18n.translate('xpack.lens.shared.barOrientation', { + defaultMessage: 'Bar orientation', + }); + const isSelected = + barOrientationOptions.find(({ value }) => value === barOrientation)?.id || + 'bar_orientation_horizontal'; + + return ( + + { + const newMode = barOrientationOptions.find(({ id }) => id === modeId); + if (newMode) { + onBarOrientationChange(newMode.value); + } + }} + /> + + ); +}; diff --git a/x-pack/plugins/lens/public/shared_components/experimental_badge.tsx b/x-pack/plugins/lens/public/shared_components/experimental_badge.tsx index 826ad9275063f..daa2ad7b3cc87 100644 --- a/x-pack/plugins/lens/public/shared_components/experimental_badge.tsx +++ b/x-pack/plugins/lens/public/shared_components/experimental_badge.tsx @@ -16,9 +16,11 @@ const defaultLabel = i18n.translate('xpack.lens.experimentalLabel', { export const ExperimentalBadge = ({ label = defaultLabel, color, + size = 's', }: { label?: string; color?: 'subdued' | undefined; + size?: 's' | 'm'; }) => { return ( @@ -28,7 +30,7 @@ export const ExperimentalBadge = ({ `} iconType="beaker" label={label} - size="s" + size={size} color={color} /> diff --git a/x-pack/plugins/lens/public/shared_components/legend/legend_settings_popover.tsx b/x-pack/plugins/lens/public/shared_components/legend/legend_settings_popover.tsx index 9498927dec722..ae82ffa43e628 100644 --- a/x-pack/plugins/lens/public/shared_components/legend/legend_settings_popover.tsx +++ b/x-pack/plugins/lens/public/shared_components/legend/legend_settings_popover.tsx @@ -16,12 +16,12 @@ import { EuiFlexItem, EuiFlexGroup, EuiComboBox, - EuiHorizontalRule, } from '@elastic/eui'; import { Position, VerticalAlignment, HorizontalAlignment, LegendValue } from '@elastic/charts'; import { LegendSize } from '@kbn/visualizations-plugin/public'; import { useDebouncedValue } from '@kbn/visualization-utils'; import { XYLegendValue } from '@kbn/visualizations-plugin/common/constants'; +import { ToolbarDivider } from '../toolbar_divider'; import { ToolbarPopover, type ToolbarPopoverProps } from '../toolbar_popover'; import { LegendLocationSettings } from './location/legend_location_settings'; import { ColumnsNumberSetting } from './layout/columns_number_setting'; @@ -230,7 +230,7 @@ export function LegendSettingsPopover - + ( + +); diff --git a/x-pack/plugins/lens/public/shared_components/toolbar_popover.tsx b/x-pack/plugins/lens/public/shared_components/toolbar_popover.tsx index acb6b36bed3e6..042853735fcd8 100644 --- a/x-pack/plugins/lens/public/shared_components/toolbar_popover.tsx +++ b/x-pack/plugins/lens/public/shared_components/toolbar_popover.tsx @@ -13,10 +13,10 @@ import { EuiIconLegend } from '@kbn/chart-icons'; const typeToIconMap: { [type: string]: string | IconType } = { legend: EuiIconLegend as IconType, - labels: 'visText', values: 'number', list: 'list', visualOptions: 'brush', + titlesAndText: 'visText', }; export type ToolbarPopoverProps = Partial & { @@ -82,9 +82,12 @@ export const ToolbarPopover: React.FC> = handleClose?.(); }} anchorPosition="downRight" + panelPaddingSize="s" {...euiPopoverProps} > - {title} + + {title} + {children} diff --git a/x-pack/plugins/lens/public/shared_components/value_labels_settings.tsx b/x-pack/plugins/lens/public/shared_components/value_labels_settings.tsx index 63cf76183778a..a5fe23e428ca5 100644 --- a/x-pack/plugins/lens/public/shared_components/value_labels_settings.tsx +++ b/x-pack/plugins/lens/public/shared_components/value_labels_settings.tsx @@ -7,7 +7,7 @@ import React, { FC } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiButtonGroup, EuiFormRow, EuiIconTip } from '@elastic/eui'; +import { EuiButtonGroup, EuiFormRow } from '@elastic/eui'; import { ValueLabelConfig } from '../../common/types'; const valueLabelsOptions: Array<{ @@ -27,8 +27,8 @@ const valueLabelsOptions: Array<{ { id: `value_labels_inside`, value: 'show', - label: i18n.translate('xpack.lens.shared.valueLabelsVisibility.inside', { - defaultMessage: 'Show', + label: i18n.translate('xpack.lens.shared.valueLabelsVisibility.show', { + defaultMessage: 'Show, if able', }), 'data-test-subj': 'lns_valueLabels_inside', }, @@ -38,41 +38,33 @@ export interface VisualOptionsProps { isVisible?: boolean; valueLabels?: ValueLabelConfig; onValueLabelChange: (newMode: ValueLabelConfig) => void; + label?: string; } +const defaultLabel = i18n.translate('xpack.lens.shared.chartValueLabelVisibilityLabel', { + defaultMessage: 'Value labels', +}); + export const ValueLabelsSettings: FC = ({ isVisible = true, valueLabels = 'hide', onValueLabelChange, + label = defaultLabel, }) => { if (!isVisible) { return null; } - const label = i18n.translate('xpack.lens.shared.chartValueLabelVisibilityLabel', { - defaultMessage: 'Labels', - }); + const isSelected = valueLabelsOptions.find(({ value }) => value === valueLabels)?.id || 'value_labels_hide'; return ( - {label}{' '} - - - } + helpText={i18n.translate('xpack.lens.shared.chartValueLabelVisibilityHelpText', { + defaultMessage: 'Values can only be shown if space is available.', + })} + label={label} > { icon: 'empty', id: 'testVis', label: faker.lorem.word(), - groupLabel: 'testVisGroup', + sortPriority: 1, + description: faker.lorem.sentence(), }, ], }, diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 65a27f0afea25..f4063747e9b77 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -975,32 +975,27 @@ export interface VisualizationType { * Visible label used in the chart switcher and above the workspace panel in collapsed state */ label: string; + description: string; /** * Optional label used in visualization type search if chart switcher is expanded and for tooltips */ fullLabel?: string; /** - * The group the visualization belongs to - */ - groupLabel: string; - /** - * Adds to the priority of the group, accumulated from all visualizations within the same group - * Total priority is used to sort groups. Higher number means higher priority (aka top of list). + * Priority of the visualization for sorting in chart switch + * Lower number means higher priority (aka top of list). * - * @default 0 */ - sortPriority?: number; - /** - * The sort order of the visualization in the grouping - * Items arranged from highest on top to lowest on bottom. - * - * @default 0 - */ - sortOrder?: number; + sortPriority: number; /** * Indicates if visualization is in the experimental stage. */ showExperimentalBadge?: boolean; + /** + * Indicates if visualization is deprecated. + */ + isDeprecated?: boolean; + subtypes?: string[]; + getCompatibleSubtype?: (seriesType?: string) => string | undefined; } export interface VisualizationDisplayOptions { @@ -1097,7 +1092,7 @@ export interface Visualization string; + getVisualizationTypeId: (state: T, layerId?: string) => string; hideFromChartSwitch?: (frame: FramePublicAPI) => boolean; /** @@ -1192,6 +1187,8 @@ export interface Visualization ) => undefined | ReactElement>; + getSubtypeSwitch?: (props: VisualizationLayerWidgetProps) => (() => JSX.Element) | null; + /** * Layer panel content rendered. This can be used to render a custom content below the title, * like a custom dataview switch diff --git a/x-pack/plugins/lens/public/visualizations/datatable/components/toolbar.tsx b/x-pack/plugins/lens/public/visualizations/datatable/components/toolbar.tsx index be5a81ef33dc0..a9d983b65e210 100644 --- a/x-pack/plugins/lens/public/visualizations/datatable/components/toolbar.tsx +++ b/x-pack/plugins/lens/public/visualizations/datatable/components/toolbar.tsx @@ -72,6 +72,7 @@ export function DataTableToolbar(props: VisualizationToolbarProps = { defaultMessage: 'Vertical Bullet', }), [GaugeShapes.SEMI_CIRCLE]: i18n.translate('xpack.lens.gaugeSemiCircle.gaugeLabel', { - defaultMessage: 'Semi-circular Gauge', + defaultMessage: 'Minor arc', }), [GaugeShapes.ARC]: i18n.translate('xpack.lens.gaugeArc.gaugeLabel', { - defaultMessage: 'Arc Gauge', + defaultMessage: 'Major arc', }), [GaugeShapes.CIRCLE]: i18n.translate('xpack.lens.gaugeCircle.gaugeLabel', { - defaultMessage: 'Circular Gauge', + defaultMessage: 'Circle', }), }; diff --git a/x-pack/plugins/lens/public/visualizations/gauge/suggestions.test.ts b/x-pack/plugins/lens/public/visualizations/gauge/suggestions.test.ts index 44cc2c4b539b9..2fa81d2073140 100644 --- a/x-pack/plugins/lens/public/visualizations/gauge/suggestions.test.ts +++ b/x-pack/plugins/lens/public/visualizations/gauge/suggestions.test.ts @@ -232,7 +232,7 @@ describe('shows suggestions', () => { shape: 'semiCircle', ticksPosition: 'auto', }, - title: 'Semi-circular Gauge', + title: 'Minor arc', }, { hide: false, @@ -247,7 +247,7 @@ describe('shows suggestions', () => { shape: 'arc', ticksPosition: 'auto', }, - title: 'Arc Gauge', + title: 'Major arc', }, { hide: false, @@ -262,7 +262,7 @@ describe('shows suggestions', () => { shape: 'circle', ticksPosition: 'auto', }, - title: 'Circular Gauge', + title: 'Circle', }, ]); }); diff --git a/x-pack/plugins/lens/public/visualizations/gauge/toolbar_component/gauge_toolbar.test.tsx b/x-pack/plugins/lens/public/visualizations/gauge/toolbar_component/gauge_toolbar.test.tsx index c12f947da946b..4de8bf449ce5b 100644 --- a/x-pack/plugins/lens/public/visualizations/gauge/toolbar_component/gauge_toolbar.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/gauge/toolbar_component/gauge_toolbar.test.tsx @@ -39,123 +39,188 @@ describe('gauge toolbar', () => { jest.useRealTimers(); }); - const renderAxisTicksSettingsAndOpen = ( - propsOverrides?: Partial> + const renderGaugeToolbarAndOpen = ( + propsOverrides?: Partial>, + toolbarName = 'Appearance' ) => { const rtlRender = render(); - const openPopover = () => userEvent.click(screen.getByRole('button', { name: 'Appearance' })); + const openPopover = () => userEvent.click(screen.getByRole('button', { name: toolbarName })); openPopover(); return { ...rtlRender, }; }; - const getTitleLabel = () => screen.getByLabelText('Title'); - const getSubtitleLabel = () => screen.getByLabelText('Subtitle'); - const getTitleSelectValue = () => screen.getByTestId('lnsToolbarGaugeLabelMajor-select'); - const getSubtitleSelectValue = () => screen.getByTestId('lnsToolbarGaugeLabelMinor-select'); + describe('gauge titles and text', () => { + const getTitleLabel = () => screen.getByLabelText('Title'); + const getSubtitleLabel = () => screen.getByLabelText('Subtitle'); + const getTitleSelectValue = () => screen.getByTestId('lnsToolbarGaugeLabelMajor-select'); + const getSubtitleSelectValue = () => screen.getByTestId('lnsToolbarGaugeLabelMinor-select'); + it('should reflect state in the UI for default props', async () => { + renderGaugeToolbarAndOpen(undefined, 'Titles and text'); + expect(getTitleLabel()).toHaveValue(''); + const titleSelect = getTitleSelectValue(); + expect(titleSelect).toHaveValue('auto'); + expect(getSubtitleLabel()).toHaveValue(''); + const subtitleSelect = getSubtitleSelectValue(); + expect(subtitleSelect).toHaveValue('none'); + }); + it('should reflect state in the UI for non-default props', async () => { + renderGaugeToolbarAndOpen( + { + state: { + ...defaultProps.state, + ticksPosition: 'bands' as const, + labelMajorMode: 'custom' as const, + labelMajor: 'new labelMajor', + labelMinor: 'new labelMinor', + }, + }, + 'Titles and text' + ); - it('should reflect state in the UI for default props', async () => { - renderAxisTicksSettingsAndOpen(); - expect(getTitleLabel()).toHaveValue(''); - const titleSelect = getTitleSelectValue(); - expect(titleSelect).toHaveValue('auto'); - expect(getSubtitleLabel()).toHaveValue(''); - const subtitleSelect = getSubtitleSelectValue(); - expect(subtitleSelect).toHaveValue('none'); - }); - it('should reflect state in the UI for non-default props', async () => { - renderAxisTicksSettingsAndOpen({ - state: { - ...defaultProps.state, - ticksPosition: 'bands' as const, - labelMajorMode: 'custom' as const, - labelMajor: 'new labelMajor', - labelMinor: 'new labelMinor', - }, + expect(getTitleLabel()).toHaveValue('new labelMajor'); + const titleSelect = getTitleSelectValue(); + expect(titleSelect).toHaveValue('custom'); + expect(getSubtitleLabel()).toHaveValue('new labelMinor'); + const subtitleSelect = getSubtitleSelectValue(); + expect(subtitleSelect).toHaveValue('custom'); }); - expect(getTitleLabel()).toHaveValue('new labelMajor'); - const titleSelect = getTitleSelectValue(); - expect(titleSelect).toHaveValue('custom'); - expect(getSubtitleLabel()).toHaveValue('new labelMinor'); - const subtitleSelect = getSubtitleSelectValue(); - expect(subtitleSelect).toHaveValue('custom'); - }); + describe('labelMajor', () => { + it('labelMajor label is disabled if labelMajor is selected to be none', () => { + renderGaugeToolbarAndOpen( + { + state: { + ...defaultProps.state, + labelMajorMode: 'none' as const, + }, + }, + 'Titles and text' + ); + expect(getTitleLabel()).toHaveValue(''); + expect(getTitleLabel()).toBeDisabled(); + const titleSelect = getTitleSelectValue(); + expect(titleSelect).toHaveValue('none'); + }); + it('labelMajor mode switches to custom when user starts typing', () => { + renderGaugeToolbarAndOpen( + { + state: { + ...defaultProps.state, + labelMajorMode: 'auto' as const, + }, + }, + 'Titles and text' + ); + const titleSelect = getTitleSelectValue(); + expect(titleSelect).toHaveValue('auto'); + expect(getTitleLabel()).toHaveValue(''); + expect(getTitleLabel()).not.toBeDisabled(); - describe('labelMajor', () => { - it('labelMajor label is disabled if labelMajor is selected to be none', () => { - renderAxisTicksSettingsAndOpen({ - state: { - ...defaultProps.state, - labelMajorMode: 'none' as const, - }, + fireEvent.change(getTitleLabel(), { target: { value: 'labelMajor' } }); + jest.advanceTimersByTime(256); + expect(getTitleLabel()).toHaveValue('labelMajor'); + const updatedTitleSelect = getTitleSelectValue(); + expect(updatedTitleSelect).toHaveValue('custom'); + expect(defaultProps.setState).toHaveBeenCalledTimes(1); + expect(defaultProps.setState).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + labelMajorMode: 'custom', + labelMajor: 'labelMajor', + }) + ); }); - expect(getTitleLabel()).toHaveValue(''); - expect(getTitleLabel()).toBeDisabled(); - const titleSelect = getTitleSelectValue(); - expect(titleSelect).toHaveValue('none'); }); - it('labelMajor mode switches to custom when user starts typing', () => { - renderAxisTicksSettingsAndOpen({ + describe('labelMinor', () => { + it('labelMinor label is enabled if labelMinor is string', () => { + renderGaugeToolbarAndOpen( + { + state: { + ...defaultProps.state, + labelMinor: 'labelMinor label', + }, + }, + 'Titles and text' + ); + expect(getSubtitleLabel()).toHaveValue('labelMinor label'); + const subtitleSelect = getSubtitleSelectValue(); + expect(subtitleSelect).toHaveValue('custom'); + expect(getSubtitleLabel()).not.toBeDisabled(); + }); + it('labelMajor mode can switch to custom', () => { + renderGaugeToolbarAndOpen( + { + state: { + ...defaultProps.state, + labelMinor: '', + }, + }, + 'Titles and text' + ); + const subtitleSelect = getSubtitleSelectValue(); + expect(subtitleSelect).toHaveValue('none'); + expect(getSubtitleLabel()).toHaveValue(''); + expect(getSubtitleLabel()).toBeDisabled(); + + fireEvent.change(getSubtitleLabel(), { target: { value: 'labelMinor label' } }); + jest.advanceTimersByTime(256); + + expect(defaultProps.setState).toHaveBeenCalledTimes(1); + expect(defaultProps.setState).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + labelMinor: 'labelMinor label', + }) + ); + }); + }); + }); + + describe('gauge shape', () => { + it('should reflect state in the UI for default props', async () => { + renderGaugeToolbarAndOpen(); + + const shapeSelect = screen.getByRole('combobox', { name: /gauge shape/i }); + expect(shapeSelect).toHaveValue('Linear'); + const verticalBulletOption = screen.getByRole('button', { name: /vertical/i }); + expect(verticalBulletOption).toHaveAttribute('aria-pressed', 'true'); + }); + it('should reflect state in the UI for non-default props', async () => { + renderGaugeToolbarAndOpen({ state: { ...defaultProps.state, - labelMajorMode: 'auto' as const, + shape: 'horizontalBullet', }, }); - const titleSelect = getTitleSelectValue(); - expect(titleSelect).toHaveValue('auto'); - expect(getTitleLabel()).toHaveValue(''); - expect(getTitleLabel()).not.toBeDisabled(); - - fireEvent.change(getTitleLabel(), { target: { value: 'labelMajor' } }); - jest.advanceTimersByTime(256); - expect(getTitleLabel()).toHaveValue('labelMajor'); - const updatedTitleSelect = getTitleSelectValue(); - expect(updatedTitleSelect).toHaveValue('custom'); + const shapeSelect = screen.getByRole('combobox', { name: /gauge shape/i }); + expect(shapeSelect).toHaveValue('Linear'); + const horizontalBulletOption = screen.getByRole('button', { name: /horizontal/i }); + expect(horizontalBulletOption).toHaveAttribute('aria-pressed', 'true'); + }); + it('should call setState when changing shape type', async () => { + renderGaugeToolbarAndOpen(); + const shapeSelect = screen.getByRole('combobox', { name: /gauge shape/i }); + fireEvent.click(shapeSelect); + fireEvent.click(screen.getByRole('option', { name: /minor arc/i })); expect(defaultProps.setState).toHaveBeenCalledTimes(1); expect(defaultProps.setState).toHaveBeenNthCalledWith( 1, expect.objectContaining({ - labelMajorMode: 'custom', - labelMajor: 'labelMajor', + shape: 'semiCircle', }) ); }); - }); - describe('labelMinor', () => { - it('labelMinor label is enabled if labelMinor is string', () => { - renderAxisTicksSettingsAndOpen({ - state: { - ...defaultProps.state, - labelMinor: 'labelMinor label', - }, - }); - expect(getSubtitleLabel()).toHaveValue('labelMinor label'); - const subtitleSelect = getSubtitleSelectValue(); - expect(subtitleSelect).toHaveValue('custom'); - expect(getSubtitleLabel()).not.toBeDisabled(); - }); - it('labelMajor mode can switch to custom', () => { - renderAxisTicksSettingsAndOpen({ - state: { - ...defaultProps.state, - labelMinor: '', - }, - }); - const subtitleSelect = getSubtitleSelectValue(); - expect(subtitleSelect).toHaveValue('none'); - expect(getSubtitleLabel()).toHaveValue(''); - expect(getSubtitleLabel()).toBeDisabled(); - - fireEvent.change(getSubtitleLabel(), { target: { value: 'labelMinor label' } }); - jest.advanceTimersByTime(256); - + it('should call setState when changing subshape type', async () => { + renderGaugeToolbarAndOpen(); + const horizontalBulletOption = screen.getByRole('button', { name: /horizontal/i }); + fireEvent.click(horizontalBulletOption); expect(defaultProps.setState).toHaveBeenCalledTimes(1); expect(defaultProps.setState).toHaveBeenNthCalledWith( 1, expect.objectContaining({ - labelMinor: 'labelMinor label', + shape: 'horizontalBullet', }) ); }); diff --git a/x-pack/plugins/lens/public/visualizations/gauge/toolbar_component/index.tsx b/x-pack/plugins/lens/public/visualizations/gauge/toolbar_component/index.tsx index a0009e4ec6861..a91aabd812b19 100644 --- a/x-pack/plugins/lens/public/visualizations/gauge/toolbar_component/index.tsx +++ b/x-pack/plugins/lens/public/visualizations/gauge/toolbar_component/index.tsx @@ -6,16 +6,166 @@ */ import React, { memo, useState } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiFormRow } from '@elastic/eui'; +import { + EuiButtonGroup, + EuiComboBox, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiIcon, + IconType, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { GaugeLabelMajorMode } from '@kbn/expression-gauge-plugin/common'; +import { GaugeLabelMajorMode, GaugeShape, GaugeShapes } from '@kbn/expression-gauge-plugin/common'; import { useDebouncedValue } from '@kbn/visualization-utils'; +import { + IconChartGaugeArcSimple, + IconChartGaugeCircleSimple, + IconChartGaugeSemiCircleSimple, + IconChartLinearSimple, +} from '@kbn/chart-icons'; import type { VisualizationToolbarProps } from '../../../types'; import { ToolbarPopover, VisLabel } from '../../../shared_components'; import './gauge_config_panel.scss'; -import type { GaugeVisualizationState } from '../constants'; +import { gaugeTitlesByType, type GaugeVisualizationState } from '../constants'; + +const PREFIX = `lns_gaugeOrientation_`; +export const bulletTypes = [ + { + id: `${PREFIX}horizontalBullet`, + label: i18n.translate('xpack.lens.gauge.bullet.orientantionHorizontal', { + defaultMessage: 'Horizontal', + }), + }, + { + id: `${PREFIX}verticalBullet`, + label: i18n.translate('xpack.lens.gauge.bullet.orientantionVertical', { + defaultMessage: 'Vertical', + }), + }, +]; + +const CHART_NAMES: Record = { + horizontalBullet: { + id: GaugeShapes.HORIZONTAL_BULLET, + icon: IconChartLinearSimple, + label: i18n.translate('xpack.lens.gaugeLinear.gaugeLabel', { + defaultMessage: 'Linear', + }), + }, + verticalBullet: { + id: GaugeShapes.VERTICAL_BULLET, + icon: IconChartLinearSimple, + label: i18n.translate('xpack.lens.gaugeLinear.gaugeLabel', { + defaultMessage: 'Linear', + }), + }, + semiCircle: { + id: GaugeShapes.SEMI_CIRCLE, + icon: IconChartGaugeSemiCircleSimple, + label: gaugeTitlesByType.semiCircle, + }, + arc: { + id: GaugeShapes.ARC, + icon: IconChartGaugeArcSimple, + label: gaugeTitlesByType.arc, + }, + circle: { + id: GaugeShapes.CIRCLE, + icon: IconChartGaugeCircleSimple, + label: gaugeTitlesByType.circle, + }, +}; + +const gaugeShapes = [ + CHART_NAMES.horizontalBullet, + CHART_NAMES.semiCircle, + CHART_NAMES.arc, + CHART_NAMES.circle, +]; export const GaugeToolbar = memo((props: VisualizationToolbarProps) => { + return ( + + + + + + + + + ); +}); + +const AppearancePopover = (props: VisualizationToolbarProps) => { + const { state, setState } = props; + + const selectedOption = CHART_NAMES[state.shape]; + const selectedBulletType = bulletTypes.find(({ id }) => id === `${PREFIX}${state.shape}`); + return ( + + + { + setState({ ...state, shape: option.value as GaugeShape }); + }} + isClearable={false} + options={gaugeShapes.map(({ id, label, icon }) => ({ + value: id, + label, + prepend: , + }))} + selectedOptions={[selectedOption]} + singleSelection={{ asPlainText: true }} + prepend={} + /> + + {(state.shape === GaugeShapes.HORIZONTAL_BULLET || + state.shape === GaugeShapes.VERTICAL_BULLET) && + selectedBulletType && ( + + { + const newBulletTypeWithPrefix = bulletTypes.find(({ id }) => id === optionId)!.id; + const newBulletType = newBulletTypeWithPrefix.replace(PREFIX, ''); + setState({ ...state, shape: newBulletType as GaugeShape }); + }} + /> + + )} + + ); +}; + +const TitlesAndTextPopover = (props: VisualizationToolbarProps) => { const { state, setState, frame } = props; const metricDimensionTitle = state.layerId && @@ -31,71 +181,66 @@ export const GaugeToolbar = memo((props: VisualizationToolbarProps - - - { - setSubtitleMode(inputValue.labelMinor ? 'custom' : 'none'); - }} - title={i18n.translate('xpack.lens.gauge.appearanceLabel', { - defaultMessage: 'Appearance', - })} - type="visualOptions" - buttonDataTestSubj="lnsVisualOptionsButton" - panelClassName="lnsGaugeToolbar__popover" - > - - { - handleInputChange({ - ...inputValue, - labelMajor: value.label, - labelMajorMode: value.mode, - }); - }} - /> - - - { - handleInputChange({ - ...inputValue, - labelMinor: value.mode === 'none' ? '' : value.label, - }); - setSubtitleMode(value.mode); - }} - /> - - - - - + { + setSubtitleMode(inputValue.labelMinor ? 'custom' : 'none'); + }} + title={i18n.translate('xpack.lens.gauge.appearanceLabel', { + defaultMessage: 'Titles and text', + })} + type="titlesAndText" + buttonDataTestSubj="lnsTextOptionsButton" + panelClassName="lnsGaugeToolbar__popover" + data-test-subj="lnsTextOptionsPopover" + > + + { + handleInputChange({ + ...inputValue, + labelMajor: value.label, + labelMajorMode: value.mode, + }); + }} + /> + + + { + handleInputChange({ + ...inputValue, + labelMinor: value.mode === 'none' ? '' : value.label, + }); + setSubtitleMode(value.mode); + }} + /> + + ); -}); +}; diff --git a/x-pack/plugins/lens/public/visualizations/gauge/visualization.tsx b/x-pack/plugins/lens/public/visualizations/gauge/visualization.tsx index ca3c65252ec47..cffa80bcc0773 100644 --- a/x-pack/plugins/lens/public/visualizations/gauge/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/gauge/visualization.tsx @@ -23,13 +23,7 @@ import { getMinValue, getValueFromAccessor, } from '@kbn/expression-gauge-plugin/public'; -import { - IconChartGaugeSemiCircle, - IconChartGaugeCircle, - IconChartGaugeArc, - IconChartHorizontalBullet, - IconChartVerticalBullet, -} from '@kbn/chart-icons'; +import { IconChartGauge } from '@kbn/chart-icons'; import { LayerTypes } from '@kbn/expression-xy-plugin/public'; import type { FormBasedPersistedState } from '../../datasources/form_based/types'; import type { @@ -39,10 +33,9 @@ import type { Suggestion, UserMessage, Visualization, - VisualizationType, } from '../../types'; import { getSuggestions } from './suggestions'; -import { GROUP_ID, LENS_GAUGE_ID, GaugeVisualizationState, gaugeTitlesByType } from './constants'; +import { GROUP_ID, LENS_GAUGE_ID, GaugeVisualizationState } from './constants'; import { GaugeToolbar } from './toolbar_component'; import { applyPaletteParams } from '../../shared_components'; import { GaugeDimensionEditor } from './dimension_editor'; @@ -57,10 +50,6 @@ import { GAUGE_MIN_NE_MAX, } from '../../user_messages_ids'; -const groupLabelForGauge = i18n.translate('xpack.lens.metric.groupLabel', { - defaultMessage: 'Goal and single value', -}); - interface GaugeVisualizationDeps { paletteService: PaletteRegistry; theme: ThemeServiceStart; @@ -72,49 +61,6 @@ export const isNumericMetric = (op: OperationMetadata) => export const isNumericDynamicMetric = (op: OperationMetadata) => isNumericMetric(op) && !op.isStaticValue; -export const CHART_NAMES: Record = { - [GaugeShapes.HORIZONTAL_BULLET]: { - id: GaugeShapes.HORIZONTAL_BULLET, - icon: IconChartHorizontalBullet, - label: gaugeTitlesByType.horizontalBullet, - groupLabel: groupLabelForGauge, - showExperimentalBadge: true, - sortOrder: 10, - }, - [GaugeShapes.VERTICAL_BULLET]: { - id: GaugeShapes.VERTICAL_BULLET, - icon: IconChartVerticalBullet, - label: gaugeTitlesByType.verticalBullet, - groupLabel: groupLabelForGauge, - showExperimentalBadge: true, - sortOrder: 10, - }, - [GaugeShapes.SEMI_CIRCLE]: { - id: GaugeShapes.SEMI_CIRCLE, - icon: IconChartGaugeSemiCircle, - label: gaugeTitlesByType.semiCircle, - groupLabel: groupLabelForGauge, - showExperimentalBadge: true, - sortOrder: 9, - }, - [GaugeShapes.ARC]: { - id: GaugeShapes.ARC, - icon: IconChartGaugeArc, - label: gaugeTitlesByType.arc, - groupLabel: groupLabelForGauge, - showExperimentalBadge: true, - sortOrder: 8, - }, - [GaugeShapes.CIRCLE]: { - id: GaugeShapes.CIRCLE, - icon: IconChartGaugeCircle, - label: gaugeTitlesByType.circle, - groupLabel: groupLabelForGauge, - showExperimentalBadge: true, - sortOrder: 7, - }, -}; - function computePaletteParams(params: CustomPaletteParams) { return { ...params, @@ -216,17 +162,30 @@ export const getGaugeVisualization = ({ paletteService, }: GaugeVisualizationDeps): Visualization => ({ id: LENS_GAUGE_ID, - + getVisualizationTypeId() { + return this.id; + }, visualizationTypes: [ - CHART_NAMES[GaugeShapes.HORIZONTAL_BULLET], - CHART_NAMES[GaugeShapes.VERTICAL_BULLET], - CHART_NAMES[GaugeShapes.SEMI_CIRCLE], - CHART_NAMES[GaugeShapes.ARC], - CHART_NAMES[GaugeShapes.CIRCLE], + { + id: LENS_GAUGE_ID, + icon: IconChartGauge, + label: i18n.translate('xpack.lens.gauge.label', { + defaultMessage: 'Gauge', + }), + showExperimentalBadge: true, + sortPriority: 7, + description: i18n.translate('xpack.lens.gauge.visualizationDescription', { + defaultMessage: 'Show progress to a goal in linear or arced style.', + }), + subtypes: [ + GaugeShapes.HORIZONTAL_BULLET, + GaugeShapes.VERTICAL_BULLET, + GaugeShapes.SEMI_CIRCLE, + GaugeShapes.ARC, + GaugeShapes.CIRCLE, + ], + }, ], - getVisualizationTypeId(state) { - return state.shape; - }, getLayerIds(state) { return [state.layerId]; }, @@ -241,8 +200,13 @@ export const getGaugeVisualization = ({ return newState; }, - getDescription(state) { - return CHART_NAMES[state.shape]; + getDescription() { + return { + icon: IconChartGauge, + label: i18n.translate('xpack.lens.gauge.label', { + defaultMessage: 'Gauge', + }), + }; }, switchVisualizationType: (visualizationTypeId, state) => { diff --git a/x-pack/plugins/lens/public/visualizations/heatmap/constants.ts b/x-pack/plugins/lens/public/visualizations/heatmap/constants.ts index 51e674048e873..6f6c2bd2403c5 100644 --- a/x-pack/plugins/lens/public/visualizations/heatmap/constants.ts +++ b/x-pack/plugins/lens/public/visualizations/heatmap/constants.ts @@ -13,10 +13,6 @@ export const LENS_HEATMAP_RENDERER = 'lens_heatmap_renderer'; export const LENS_HEATMAP_ID = 'lnsHeatmap'; export const DEFAULT_PALETTE_NAME = 'temperature'; -const groupLabel = i18n.translate('xpack.lens.heatmap.groupLabel', { - defaultMessage: 'Heatmap', -}); - export const CHART_SHAPES = { HEATMAP: 'heatmap', } as const; @@ -28,7 +24,6 @@ export const CHART_NAMES = { label: i18n.translate('xpack.lens.heatmap.heatmapLabel', { defaultMessage: 'Heat map', }), - groupLabel, }, }; diff --git a/x-pack/plugins/lens/public/visualizations/heatmap/toolbar_component.tsx b/x-pack/plugins/lens/public/visualizations/heatmap/toolbar_component.tsx index 9d5e081ba351d..cd1dd560954c8 100644 --- a/x-pack/plugins/lens/public/visualizations/heatmap/toolbar_component.tsx +++ b/x-pack/plugins/lens/public/visualizations/heatmap/toolbar_component.tsx @@ -24,6 +24,10 @@ import type { HeatmapVisualizationState } from './types'; import { getDefaultVisualValuesForLayer } from '../../shared_components/datasource_default_values'; import './toolbar_component.scss'; +const PANEL_STYLE = { + width: '460px', +}; + const legendOptions: Array<{ id: string; value: 'auto' | 'show' | 'hide'; label: string }> = [ { id: `heatmap_legend_show`, @@ -58,79 +62,25 @@ export const HeatmapToolbar = memo( return ( - - - { - setState({ - ...state, - gridConfig: { ...state.gridConfig, isCellLabelVisible: newMode === 'show' }, - }); - }} - /> - - - { - const newMode = legendOptions.find(({ id }) => id === optionId)!.value; - if (newMode === 'show') { - setState({ - ...state, - legend: { ...state.legend, isVisible: true }, - }); - } else if (newMode === 'hide') { - setState({ - ...state, - legend: { ...state.legend, isVisible: false }, - }); - } - }} - position={state?.legend.position} - onPositionChange={(id) => { - setState({ - ...state, - legend: { ...state.legend, position: id as Position }, - }); - }} - maxLines={state?.legend.maxLines} - onMaxLinesChange={(val) => { - setState({ - ...state, - legend: { ...state.legend, maxLines: val }, - }); - }} - shouldTruncate={state?.legend.shouldTruncate ?? defaultTruncationValue} - onTruncateLegendChange={() => { - const current = state.legend.shouldTruncate ?? defaultTruncationValue; + + { setState({ ...state, - legend: { ...state.legend, shouldTruncate: !current }, + gridConfig: { ...state.gridConfig, isCellLabelVisible: newMode === 'show' }, }); }} - legendSize={legendSize} - onLegendSizeChange={(newLegendSize) => { - setState({ - ...state, - legend: { - ...state.legend, - legendSize: newLegendSize, - }, - }); - }} - showAutoLegendSizeOption={hadAutoLegendSize} /> - + @@ -229,6 +179,59 @@ export const HeatmapToolbar = memo( + + { + const newMode = legendOptions.find(({ id }) => id === optionId)!.value; + if (newMode === 'show') { + setState({ + ...state, + legend: { ...state.legend, isVisible: true }, + }); + } else if (newMode === 'hide') { + setState({ + ...state, + legend: { ...state.legend, isVisible: false }, + }); + } + }} + position={state?.legend.position} + onPositionChange={(id) => { + setState({ + ...state, + legend: { ...state.legend, position: id as Position }, + }); + }} + maxLines={state?.legend.maxLines} + onMaxLinesChange={(val) => { + setState({ + ...state, + legend: { ...state.legend, maxLines: val }, + }); + }} + shouldTruncate={state?.legend.shouldTruncate ?? defaultTruncationValue} + onTruncateLegendChange={() => { + const current = state.legend.shouldTruncate ?? defaultTruncationValue; + setState({ + ...state, + legend: { ...state.legend, shouldTruncate: !current }, + }); + }} + legendSize={legendSize} + onLegendSizeChange={(newLegendSize) => { + setState({ + ...state, + legend: { + ...state.legend, + legendSize: newLegendSize, + }, + }); + }} + showAutoLegendSizeOption={hadAutoLegendSize} + /> + ); } diff --git a/x-pack/plugins/lens/public/visualizations/heatmap/visualization.tsx b/x-pack/plugins/lens/public/visualizations/heatmap/visualization.tsx index d434d58bdfa75..f4f3f7b652cb7 100644 --- a/x-pack/plugins/lens/public/visualizations/heatmap/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/heatmap/visualization.tsx @@ -40,10 +40,6 @@ import { getSafePaletteParams } from './utils'; import { FormBasedPersistedState } from '../..'; import { HEATMAP_RENDER_ARRAY_VALUES, HEATMAP_X_MISSING_AXIS } from '../../user_messages_ids'; -const groupLabelForHeatmap = i18n.translate('xpack.lens.heatmapVisualization.heatmapGroupLabel', { - defaultMessage: 'Magnitude', -}); - interface HeatmapVisualizationDeps { paletteService: PaletteRegistry; theme: ThemeServiceStart; @@ -108,6 +104,9 @@ export const getHeatmapVisualization = ({ }: HeatmapVisualizationDeps): Visualization => ({ id: LENS_HEATMAP_ID, + getVisualizationTypeId(state) { + return state.shape; + }, visualizationTypes: [ { id: 'heatmap', @@ -115,16 +114,13 @@ export const getHeatmapVisualization = ({ label: i18n.translate('xpack.lens.heatmapVisualization.heatmapLabel', { defaultMessage: 'Heat map', }), - groupLabel: groupLabelForHeatmap, - showExperimentalBadge: false, - sortPriority: 1, + sortPriority: 8, + description: i18n.translate('xpack.lens.heatmap.visualizationDescription', { + defaultMessage: 'Show density or distribution across two dimensions.', + }), }, ], - getVisualizationTypeId(state) { - return state.shape; - }, - getLayerIds(state) { return [state.layerId]; }, diff --git a/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_config_panel/index.tsx b/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_config_panel/index.tsx index 4397f47acdcec..731fc526ba5df 100644 --- a/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_config_panel/index.tsx +++ b/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_config_panel/index.tsx @@ -6,22 +6,21 @@ */ import React, { memo } from 'react'; -import { EuiFlexGroup, EuiFlexItem, htmlIdGenerator } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import type { VisualizationToolbarProps } from '../../../types'; import type { LegacyMetricState } from '../../../../common/types'; +import { TitlesAndTextOptionsPopover } from './titles_and_text_options_popover'; -import { AppearanceOptionsPopover } from './appearance_options_popover'; - -export const MetricToolbar = memo(function MetricToolbar( - props: VisualizationToolbarProps -) { - const { state, setState, frame } = props; - +export const MetricToolbar = memo(function MetricToolbar({ + state, + setState, + frame, +}: VisualizationToolbarProps) { return ( - ); }); - -export const idPrefix = htmlIdGenerator()(); diff --git a/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_config_panel/appearance_options_popover.tsx b/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_config_panel/titles_and_text_options_popover.tsx similarity index 72% rename from x-pack/plugins/lens/public/visualizations/legacy_metric/metric_config_panel/appearance_options_popover.tsx rename to x-pack/plugins/lens/public/visualizations/legacy_metric/metric_config_panel/titles_and_text_options_popover.tsx index ad3215634a261..e322ef99c31d3 100644 --- a/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_config_panel/appearance_options_popover.tsx +++ b/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_config_panel/titles_and_text_options_popover.tsx @@ -14,30 +14,30 @@ import { FramePublicAPI } from '../../../types'; import type { LegacyMetricState } from '../../../../common/types'; import { TextFormattingOptions } from './text_formatting_options'; -export interface VisualOptionsPopoverProps { +export interface TitlesAndTextPopoverProps { state: LegacyMetricState; setState: (newState: LegacyMetricState) => void; datasourceLayers: FramePublicAPI['datasourceLayers']; } -export const AppearanceOptionsPopover: React.FC = ({ +export const TitlesAndTextOptionsPopover: React.FC = ({ state, setState, }) => { return ( diff --git a/x-pack/plugins/lens/public/visualizations/legacy_metric/visualization.tsx b/x-pack/plugins/lens/public/visualizations/legacy_metric/visualization.tsx index 2cbe3449f9511..35983e52f80da 100644 --- a/x-pack/plugins/lens/public/visualizations/legacy_metric/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/legacy_metric/visualization.tsx @@ -157,6 +157,9 @@ export const getLegacyMetricVisualization = ({ }): Visualization => ({ id: 'lnsLegacyMetric', + getVisualizationTypeId() { + return this.id; + }, visualizationTypes: [ { id: 'lnsLegacyMetric', @@ -164,8 +167,10 @@ export const getLegacyMetricVisualization = ({ label: i18n.translate('xpack.lens.legacyMetric.label', { defaultMessage: 'Legacy Metric', }), - groupLabel: i18n.translate('xpack.lens.legacyMetric.groupLabel', { - defaultMessage: 'Goal and single value', + isDeprecated: true, + sortPriority: 100, + description: i18n.translate('xpack.lens.legacyMetric.visualizationDescription', { + defaultMessage: 'Present individual key metrics or KPIs.', }), }, ], @@ -175,10 +180,6 @@ export const getLegacyMetricVisualization = ({ ); }, - getVisualizationTypeId() { - return 'lnsLegacyMetric'; - }, - clearLayer(state) { return { ...state, diff --git a/x-pack/plugins/lens/public/visualizations/metric/toolbar/titles_and_text_popover.test.tsx b/x-pack/plugins/lens/public/visualizations/metric/toolbar/titles_and_text_popover.test.tsx index bcb529131c0f7..871c2b2187e9e 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/toolbar/titles_and_text_popover.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/metric/toolbar/titles_and_text_popover.test.tsx @@ -74,7 +74,7 @@ describe('TitlesAndTextPopover', () => { ...fullState, breakdownByAccessor: undefined, }); - const labelOptionsButton = screen.getByTestId('lnsTitlesTextButton'); + const labelOptionsButton = screen.getByTestId('lnsTextOptionsButton'); labelOptionsButton.click(); const newSubtitle = 'new subtitle hey'; @@ -108,7 +108,7 @@ describe('TitlesAndTextPopover', () => { it('should set titlesTextAlign', async () => { renderToolbarOptions({ ...fullState }); - const textOptionsButton = screen.getByTestId('lnsTitlesTextButton'); + const textOptionsButton = screen.getByTestId('lnsTextOptionsButton'); textOptionsButton.click(); const titlesAlignBtnGroup = new EuiButtonGroupTestHarness('lens-titles-alignment-btn'); @@ -126,7 +126,7 @@ describe('TitlesAndTextPopover', () => { it('should set valuesTextAlign', async () => { renderToolbarOptions({ ...fullState }); - const textOptionsButton = screen.getByTestId('lnsTitlesTextButton'); + const textOptionsButton = screen.getByTestId('lnsTextOptionsButton'); textOptionsButton.click(); const valueAlignBtnGroup = new EuiButtonGroupTestHarness('lens-values-alignment-btn'); @@ -144,7 +144,7 @@ describe('TitlesAndTextPopover', () => { it('should set valueFontMode', async () => { renderToolbarOptions({ ...fullState }); - const textOptionsButton = screen.getByTestId('lnsTitlesTextButton'); + const textOptionsButton = screen.getByTestId('lnsTextOptionsButton'); textOptionsButton.click(); const modeBtnGroup = new EuiButtonGroupTestHarness('lens-value-font-mode-btn'); @@ -159,7 +159,7 @@ describe('TitlesAndTextPopover', () => { it('should set iconAlign', async () => { renderToolbarOptions({ ...fullState, icon: 'sortUp' }); - const textOptionsButton = screen.getByTestId('lnsTitlesTextButton'); + const textOptionsButton = screen.getByTestId('lnsTextOptionsButton'); textOptionsButton.click(); const iconAlignBtnGroup = new EuiButtonGroupTestHarness('lens-icon-alignment-btn'); @@ -174,7 +174,7 @@ describe('TitlesAndTextPopover', () => { it.each([undefined, 'empty'])('should hide iconAlign option when icon is %j', async (icon) => { renderToolbarOptions({ ...fullState, icon }); - const textOptionsButton = screen.getByTestId('lnsTitlesTextButton'); + const textOptionsButton = screen.getByTestId('lnsTextOptionsButton'); textOptionsButton.click(); expect(screen.queryByTestId('lens-icon-alignment-btn')).not.toBeInTheDocument(); diff --git a/x-pack/plugins/lens/public/visualizations/metric/toolbar/titles_and_text_popover.tsx b/x-pack/plugins/lens/public/visualizations/metric/toolbar/titles_and_text_popover.tsx index 3fdbf2c28e242..88428fbe8ca86 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/toolbar/titles_and_text_popover.tsx +++ b/x-pack/plugins/lens/public/visualizations/metric/toolbar/titles_and_text_popover.tsx @@ -32,9 +32,9 @@ export const TitlesAndTextPopover: FC = ({ title={i18n.translate('xpack.lens.metric.toolbarTitlesText.label', { defaultMessage: 'Titles and text', })} - type="labels" + type="titlesAndText" groupPosition={groupPosition} - buttonDataTestSubj="lnsTitlesTextButton" + buttonDataTestSubj="lnsTextOptionsButton" > {!state.breakdownByAccessor && ( export const metricLabel = i18n.translate('xpack.lens.metric.label', { defaultMessage: 'Metric', }); -const metricGroupLabel = i18n.translate('xpack.lens.metric.groupLabel', { - defaultMessage: 'Goal and single value', -}); const getMetricLayerConfiguration = ( props: VisualizationConfigProps @@ -304,21 +301,22 @@ export const getMetricVisualization = ({ }): Visualization => ({ id: LENS_METRIC_ID, + getVisualizationTypeId() { + return this.id; + }, visualizationTypes: [ { id: LENS_METRIC_ID, icon: IconChartMetric, label: metricLabel, - groupLabel: metricGroupLabel, showExperimentalBadge: true, - sortPriority: 3, + sortPriority: 4, + description: i18n.translate('xpack.lens.metric.visualizationDescription', { + defaultMessage: 'Present individual key metrics or KPIs.', + }), }, ], - getVisualizationTypeId() { - return LENS_METRIC_ID; - }, - clearLayer(state) { const newState = { ...state }; delete newState.subtitle; diff --git a/x-pack/plugins/lens/public/visualizations/partition/partition_charts_meta.ts b/x-pack/plugins/lens/public/visualizations/partition/partition_charts_meta.ts index ad95c0ca7efdc..856b5e5a10336 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/partition_charts_meta.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/partition_charts_meta.ts @@ -6,15 +6,17 @@ */ import { i18n } from '@kbn/i18n'; -import type { EuiIconProps } from '@elastic/eui'; +import type { EuiIconProps, IconType } from '@elastic/eui'; import type { DatatableColumn } from '@kbn/expressions-plugin/common'; import { - IconChartDonut, IconChartPie, IconChartTreemap, IconChartMosaic, IconChartWaffle, + IconDonutHoleSmall, + IconDonutHoleMedium, + IconDonutHoleLarge, } from '@kbn/chart-icons'; import type { PartitionLegendValue } from '@kbn/visualizations-plugin/common/constants'; import { LegendValue } from '@elastic/charts'; @@ -23,25 +25,28 @@ import { CategoryDisplay, NumberDisplay } from '../../../common/constants'; import type { PieChartType } from '../../../common/types'; interface PartitionChartMeta { + id: string; icon: ({ title, titleId, ...props }: Omit) => JSX.Element; label: string; - groupLabel: string; maxBuckets: number; - isExperimental?: boolean; + showExperimentalBadge?: boolean; + sortPriority: number; + description: string; toolbarPopover: { isDisabled?: boolean; categoryOptions: Array<{ - value: SharedPieLayerState['categoryDisplay']; - inputDisplay: string; + id: SharedPieLayerState['categoryDisplay']; + label: string; }>; numberOptions: Array<{ - value: SharedPieLayerState['numberDisplay']; - inputDisplay: string; + id: SharedPieLayerState['numberDisplay']; + label: string; }>; emptySizeRatioOptions?: Array<{ id: string; - value: EmptySizeRatios; + value: EmptySizeRatios | 0; label: string; + icon?: IconType; }>; }; legend: { @@ -52,70 +57,74 @@ interface PartitionChartMeta { }; } -const groupLabel = i18n.translate('xpack.lens.pie.groupLabel', { - defaultMessage: 'Proportion', -}); - const categoryOptions: PartitionChartMeta['toolbarPopover']['categoryOptions'] = [ { - value: CategoryDisplay.DEFAULT, - inputDisplay: i18n.translate('xpack.lens.pieChart.showCategoriesLabel', { - defaultMessage: 'Inside or outside', + id: CategoryDisplay.HIDE, + label: i18n.translate('xpack.lens.pieChart.categoriesHideLabel', { + defaultMessage: 'Hide', }), }, { - value: CategoryDisplay.INSIDE, - inputDisplay: i18n.translate('xpack.lens.pieChart.fitInsideOnlyLabel', { - defaultMessage: 'Inside only', + id: CategoryDisplay.INSIDE, + label: i18n.translate('xpack.lens.pieChart.categoriesInsideOnlyLabel', { + defaultMessage: 'Inside', }), }, { - value: CategoryDisplay.HIDE, - inputDisplay: i18n.translate('xpack.lens.pieChart.categoriesInLegendLabel', { - defaultMessage: 'Hide labels', + id: CategoryDisplay.DEFAULT, + label: i18n.translate('xpack.lens.pieChart.autoCategoriesLabel', { + defaultMessage: 'Auto', }), }, ]; const categoryOptionsTreemap: PartitionChartMeta['toolbarPopover']['categoryOptions'] = [ { - value: CategoryDisplay.DEFAULT, - inputDisplay: i18n.translate('xpack.lens.pieChart.showTreemapCategoriesLabel', { - defaultMessage: 'Show labels', + id: CategoryDisplay.HIDE, + label: i18n.translate('xpack.lens.pieChart.hideTreemapCategoriesLabel', { + defaultMessage: 'Hide', }), }, { - value: CategoryDisplay.HIDE, - inputDisplay: i18n.translate('xpack.lens.pieChart.categoriesInLegendLabel', { - defaultMessage: 'Hide labels', + id: CategoryDisplay.DEFAULT, + label: i18n.translate('xpack.lens.pieChart.showTreemapCategoriesLabel', { + defaultMessage: 'Show', }), }, ]; const numberOptions: PartitionChartMeta['toolbarPopover']['numberOptions'] = [ { - value: NumberDisplay.HIDDEN, - inputDisplay: i18n.translate('xpack.lens.pieChart.hiddenNumbersLabel', { - defaultMessage: 'Hide from chart', + id: NumberDisplay.HIDDEN, + label: i18n.translate('xpack.lens.pieChart.hiddenNumbersLabel', { + defaultMessage: 'Hide', }), }, { - value: NumberDisplay.PERCENT, - inputDisplay: i18n.translate('xpack.lens.pieChart.showPercentValuesLabel', { - defaultMessage: 'Show percent', + id: NumberDisplay.VALUE, + label: i18n.translate('xpack.lens.pieChart.integerLabel', { + defaultMessage: 'Integer', }), }, { - value: NumberDisplay.VALUE, - inputDisplay: i18n.translate('xpack.lens.pieChart.showFormatterValuesLabel', { - defaultMessage: 'Show value', + id: NumberDisplay.PERCENT, + label: i18n.translate('xpack.lens.pieChart.percentageLabel', { + defaultMessage: 'Percentage', }), }, ]; const emptySizeRatioOptions: PartitionChartMeta['toolbarPopover']['emptySizeRatioOptions'] = [ + { + id: 'emptySizeRatioOption-none', + value: 0, + label: i18n.translate('xpack.lens.pieChart.emptySizeRatioOptions.none', { + defaultMessage: 'None', + }), + }, { id: 'emptySizeRatioOption-small', + icon: IconDonutHoleSmall, value: EmptySizeRatios.SMALL, label: i18n.translate('xpack.lens.pieChart.emptySizeRatioOptions.small', { defaultMessage: 'Small', @@ -124,6 +133,7 @@ const emptySizeRatioOptions: PartitionChartMeta['toolbarPopover']['emptySizeRati { id: 'emptySizeRatioOption-medium', value: EmptySizeRatios.MEDIUM, + icon: IconDonutHoleMedium, label: i18n.translate('xpack.lens.pieChart.emptySizeRatioOptions.medium', { defaultMessage: 'Medium', }), @@ -131,50 +141,71 @@ const emptySizeRatioOptions: PartitionChartMeta['toolbarPopover']['emptySizeRati { id: 'emptySizeRatioOption-large', value: EmptySizeRatios.LARGE, + icon: IconDonutHoleLarge, label: i18n.translate('xpack.lens.pieChart.emptySizeRatioOptions.large', { defaultMessage: 'Large', }), }, ]; +const sharedPieDonutOptions: Omit = { + icon: IconChartPie, + label: i18n.translate('xpack.lens.pie.pielabel', { + defaultMessage: 'Pie', + }), + maxBuckets: 3, + toolbarPopover: { + categoryOptions, + numberOptions, + emptySizeRatioOptions, + }, + legend: { + getShowLegendDefault: (bucketColumns) => bucketColumns.length > 1, + }, + sortPriority: 6, + description: i18n.translate('xpack.lens.pie.visualizationDescription', { + defaultMessage: 'Display parts of a whole in a circular format.', + }), +}; + export const PartitionChartsMeta: Record = { + pie: { + id: 'pie', + ...sharedPieDonutOptions, + }, donut: { - icon: IconChartDonut, - label: i18n.translate('xpack.lens.pie.donutLabel', { - defaultMessage: 'Donut', - }), - groupLabel, - maxBuckets: 3, - toolbarPopover: { - categoryOptions, - numberOptions, - emptySizeRatioOptions, - }, - legend: { - getShowLegendDefault: (bucketColumns) => bucketColumns.length > 1, - }, + id: 'donut', + ...sharedPieDonutOptions, }, - pie: { - icon: IconChartPie, - label: i18n.translate('xpack.lens.pie.pielabel', { - defaultMessage: 'Pie', + waffle: { + id: 'waffle', + icon: IconChartWaffle, + label: i18n.translate('xpack.lens.pie.wafflelabel', { + defaultMessage: 'Waffle', }), - groupLabel, - maxBuckets: 3, + maxBuckets: 1, toolbarPopover: { - categoryOptions, - numberOptions, + isDisabled: true, + categoryOptions: [], + numberOptions: [], }, legend: { - getShowLegendDefault: (bucketColumns) => bucketColumns.length > 1, + flat: true, + defaultLegendStats: [LegendValue.Value], + hideNestedLegendSwitch: true, + getShowLegendDefault: () => true, }, + sortPriority: 9, + description: i18n.translate('xpack.lens.waffle.visualizationDescription', { + defaultMessage: 'Represent data proportions via a grid of colored cells.', + }), }, treemap: { + id: 'treemap', icon: IconChartTreemap, label: i18n.translate('xpack.lens.pie.treemaplabel', { defaultMessage: 'Treemap', }), - groupLabel, maxBuckets: 2, toolbarPopover: { categoryOptions: categoryOptionsTreemap, @@ -183,13 +214,17 @@ export const PartitionChartsMeta: Record = { legend: { getShowLegendDefault: () => false, }, + sortPriority: 11, + description: i18n.translate('xpack.lens.treemap.visualizationDescription', { + defaultMessage: 'Use nested rectangles to show proportions and hierarchy.', + }), }, mosaic: { + id: 'mosaic', icon: IconChartMosaic, label: i18n.translate('xpack.lens.pie.mosaiclabel', { defaultMessage: 'Mosaic', }), - groupLabel, maxBuckets: 2, toolbarPopover: { categoryOptions: [], @@ -198,24 +233,19 @@ export const PartitionChartsMeta: Record = { legend: { getShowLegendDefault: () => false, }, - }, - waffle: { - icon: IconChartWaffle, - label: i18n.translate('xpack.lens.pie.wafflelabel', { - defaultMessage: 'Waffle', + sortPriority: 13, + description: i18n.translate('xpack.lens.mosaic.visualizationDescription', { + defaultMessage: 'Show proportions of categorical data with rectangles.', }), - groupLabel, - maxBuckets: 1, - toolbarPopover: { - isDisabled: true, - categoryOptions: [], - numberOptions: [], - }, - legend: { - flat: true, - defaultLegendStats: [LegendValue.Value], - hideNestedLegendSwitch: true, - getShowLegendDefault: () => true, - }, }, }; + +export const visualizationTypes = [ + { + ...PartitionChartsMeta.pie, + subtypes: ['pie', 'donut'], + }, + PartitionChartsMeta.waffle, + PartitionChartsMeta.treemap, + PartitionChartsMeta.mosaic, +]; diff --git a/x-pack/plugins/lens/public/visualizations/partition/toolbar.tsx b/x-pack/plugins/lens/public/visualizations/partition/toolbar.tsx index 436c307ab1007..8262231ceb5d1 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/toolbar.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/toolbar.tsx @@ -11,10 +11,12 @@ import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, EuiFormRow, - EuiSuperSelect, - EuiRange, - EuiHorizontalRule, + EuiComboBox, + EuiIcon, + EuiFieldNumber, EuiButtonGroup, + EuiFlexItem, + EuiComboBoxOptionOption, } from '@elastic/eui'; import { LegendValue, Position } from '@elastic/charts'; import { LegendSize } from '@kbn/visualizations-plugin/public'; @@ -22,8 +24,8 @@ import { useDebouncedValue } from '@kbn/visualization-utils'; import { type PartitionLegendValue } from '@kbn/visualizations-plugin/common/constants'; import { DEFAULT_PERCENT_DECIMALS } from './constants'; import { PartitionChartsMeta } from './partition_charts_meta'; -import { PieVisualizationState, SharedPieLayerState } from '../../../common/types'; -import { LegendDisplay } from '../../../common/constants'; +import { EmptySizeRatios, PieVisualizationState, SharedPieLayerState } from '../../../common/types'; +import { LegendDisplay, NumberDisplay } from '../../../common/constants'; import { VisualizationToolbarProps } from '../../types'; import { ToolbarPopover, LegendSettingsPopover } from '../../shared_components'; import { getDefaultVisualValuesForLayer } from '../../shared_components/datasource_default_values'; @@ -66,8 +68,12 @@ const legendOptions: Array<{ }, ]; -const emptySizeRatioLabel = i18n.translate('xpack.lens.pieChart.emptySizeRatioLabel', { - defaultMessage: 'Inner area size', +const PANEL_STYLE = { + width: '500px', +}; + +const emptySizeRatioLabel = i18n.translate('xpack.lens.pieChart.donutHole', { + defaultMessage: 'Donut hole', }); export function PieToolbar(props: VisualizationToolbarProps) { @@ -151,12 +157,26 @@ export function PieToolbar(props: VisualizationToolbarProps { - const emptySizeRatio = emptySizeRatioOptions?.find(({ id }) => id === sizeId)?.value; - onStateChange({ emptySizeRatio }); + ([option]: Array>) => { + if (option.value === 'none') { + setState({ ...state, shape: 'pie', layers: [{ ...layer, emptySizeRatio: undefined }] }); + } else { + const emptySizeRatio = emptySizeRatioOptions?.find(({ id }) => id === option.value)?.value; + setState({ + ...state, + shape: 'donut', + layers: [{ ...layer, emptySizeRatio }], + }); + } }, - [emptySizeRatioOptions, onStateChange] + [emptySizeRatioOptions, layer, setState, state] ); + const selectedOption = emptySizeRatioOptions + ? emptySizeRatioOptions.find( + ({ value }) => + value === (state.shape === 'pie' ? 0 : layer.emptySizeRatio ?? EmptySizeRatios.SMALL) + ) + : undefined; if (!layer) { return null; @@ -168,122 +188,137 @@ export function PieToolbar(props: VisualizationToolbarProps - - {categoryOptions.length ? ( - - - - ) : null} - - {numberOptions.length && layer.categoryDisplay !== 'hide' ? ( - + {emptySizeRatioOptions?.length && selectedOption ? ( + + - - - ) : null} - - {numberOptions.length + categoryOptions.length ? : null} - - - - - - {emptySizeRatioOptions?.length ? ( + + ({ + value: id, + label, + prepend: icon ? : undefined, + }))} + selectedOptions={[{ value: selectedOption.id, label: selectedOption.label }]} + singleSelection={{ asPlainText: true }} + prepend={selectedOption?.icon ? : undefined} + /> + + + + ) : null} + - - value === layer.emptySizeRatio)?.id ?? - 'emptySizeRatioOption-small' - } - onChange={onEmptySizeRatioChange} - data-test-subj="lnsEmptySizeRatioButtonGroup" - /> - + {categoryOptions.length ? ( + + + + ) : null} + + {numberOptions.length && layer.categoryDisplay !== 'hide' ? ( + <> + + + + {layer.numberDisplay === NumberDisplay.PERCENT && ( + + + + )} + + ) : null} - ) : null} - - legendOptions={legendOptions} - mode={layer.legendDisplay} - onDisplayChange={onLegendDisplayChange} - legendStats={getLegendStats(layer, state.shape)} - allowedLegendStats={ - PartitionChartsMeta[state.shape]?.legend.defaultLegendStats - ? partitionLegendValues - : undefined - } - onLegendStatsChange={onLegendStatsChange} - position={layer.legendPosition} - onPositionChange={onLegendPositionChange} - renderNestedLegendSwitch={ - !PartitionChartsMeta[state.shape]?.legend.hideNestedLegendSwitch && - layer.primaryGroups.length + (layer.secondaryGroups?.length ?? 0) > 1 - } - nestedLegend={Boolean(layer.nestedLegend)} - onNestedLegendChange={onNestedLegendChange} - shouldTruncate={layer.truncateLegend ?? defaultTruncationValue} - onTruncateLegendChange={onTruncateLegendChange} - maxLines={layer?.legendMaxLines} - onMaxLinesChange={onLegendMaxLinesChange} - legendSize={legendSize} - onLegendSizeChange={onLegendSizeChange} - showAutoLegendSizeOption={hadAutoLegendSize} - /> + + + + groupPosition={'none'} + legendOptions={legendOptions} + mode={layer.legendDisplay} + onDisplayChange={onLegendDisplayChange} + legendStats={getLegendStats(layer, state.shape)} + allowedLegendStats={ + PartitionChartsMeta[state.shape]?.legend.defaultLegendStats + ? partitionLegendValues + : undefined + } + onLegendStatsChange={onLegendStatsChange} + position={layer.legendPosition} + onPositionChange={onLegendPositionChange} + renderNestedLegendSwitch={ + !PartitionChartsMeta[state.shape]?.legend.hideNestedLegendSwitch && + layer.primaryGroups.length + (layer.secondaryGroups?.length ?? 0) > 1 + } + nestedLegend={Boolean(layer.nestedLegend)} + onNestedLegendChange={onNestedLegendChange} + shouldTruncate={layer.truncateLegend ?? defaultTruncationValue} + onTruncateLegendChange={onTruncateLegendChange} + maxLines={layer?.legendMaxLines} + onMaxLinesChange={onLegendMaxLinesChange} + legendSize={legendSize} + onLegendSizeChange={onLegendSizeChange} + showAutoLegendSizeOption={hadAutoLegendSize} + /> + ); } -const DecimalPlaceSlider = ({ +const DecimalPlaceInput = ({ value, setValue, }: { @@ -298,12 +333,14 @@ const DecimalPlaceSlider = ({ { allowFalsyValue: true } ); return ( - { handleInputChange(Number(e.currentTarget.value)); diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx index 71daba931f23d..661921caaa1ef 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx @@ -45,7 +45,7 @@ import { PieChartTypes, } from '../../../common/constants'; import { suggestions } from './suggestions'; -import { PartitionChartsMeta } from './partition_charts_meta'; +import { PartitionChartsMeta, visualizationTypes } from './partition_charts_meta'; import { PieToolbar } from './toolbar'; import { DimensionDataExtraEditor, DimensionEditor } from './dimension_editor'; import { LayerSettings } from './layer_settings'; @@ -131,17 +131,9 @@ export const getPieVisualization = ({ kibanaTheme: ThemeServiceStart; }): Visualization => ({ id: 'lnsPie', - - visualizationTypes: Object.entries(PartitionChartsMeta).map(([key, meta]) => ({ - id: key, - icon: meta.icon, - label: meta.label, - groupLabel: meta.groupLabel, - showExperimentalBadge: meta.isExperimental, - })), - + visualizationTypes, getVisualizationTypeId(state) { - return state.shape; + return state.shape === 'donut' ? 'pie' : state.shape; }, getLayerIds(state) { diff --git a/x-pack/plugins/lens/public/visualizations/tagcloud/tagcloud_toolbar/tagcloud_toolbar.tsx b/x-pack/plugins/lens/public/visualizations/tagcloud/tagcloud_toolbar/tagcloud_toolbar.tsx index 406fe411665e4..12e4bbcab4021 100644 --- a/x-pack/plugins/lens/public/visualizations/tagcloud/tagcloud_toolbar/tagcloud_toolbar.tsx +++ b/x-pack/plugins/lens/public/visualizations/tagcloud/tagcloud_toolbar/tagcloud_toolbar.tsx @@ -61,6 +61,7 @@ export function TagcloudToolbar(props: VisualizationToolbarProps) })} type="visualOptions" buttonDataTestSubj="lnsVisualOptionsButton" + data-test-subj="lnsVisualOptionsPopover" > ) /> => ({ id: 'lnsTagcloud', + getVisualizationTypeId() { + return this.id; + }, visualizationTypes: [ { id: 'lnsTagcloud', icon: IconChartTagcloud, label: TAGCLOUD_LABEL, - groupLabel: i18n.translate('xpack.lens.pie.groupLabel', { - defaultMessage: 'Proportion', + sortPriority: 12, + description: i18n.translate('xpack.lens.tagcloud.visualizationDescription', { + defaultMessage: 'Visualize text data frequency or importance.', }), }, ], - getVisualizationTypeId() { - return 'lnsTagcloud'; - }, - clearLayer(state) { const newState = { ...state, diff --git a/x-pack/plugins/lens/public/visualizations/xy/add_layer.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/add_layer.test.tsx index 5ecef7e45eebe..9dd12432e5889 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/add_layer.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/add_layer.test.tsx @@ -79,7 +79,7 @@ describe('AddLayerButton', () => { return within( screen.getByTestId('contextMenuPanelTitleButton').parentElement as HTMLElement ) - .getAllByRole('button') + .getAllByTestId('lnsChartSwitch-option-label') .map((el) => el.textContent); }, }; @@ -95,17 +95,7 @@ describe('AddLayerButton', () => { clickAddLayer(); clickVisualizationButton(); await waitForSeriesOptions(); - - expect(getSeriesTypeOptions()).toEqual([ - 'Select visualization type', - 'Bar vertical', - 'Bar vertical stacked', - 'Bar vertical percentage', - 'Area', - 'Area stacked', - 'Area percentage', - 'Line', - ]); + expect(getSeriesTypeOptions()).toEqual(['Bar', 'Area', 'Line']); }); it('calls addLayer with a proper series type when button is clicked', async () => { const { diff --git a/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx b/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx index 3a22d50c6d066..2a9f125bfa663 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx @@ -13,17 +13,22 @@ import { EuiContextMenu, EuiFlexItem, EuiFlexGroup, + IconType, + transparentize, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { LayerTypes } from '@kbn/expression-xy-plugin/public'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; +import { css } from '@emotion/react'; +import { euiThemeVars } from '@kbn/ui-theme'; import { AddLayerFunction, VisualizationLayerDescription } from '../../types'; import { LoadAnnotationLibraryFlyout } from './load_annotation_library_flyout'; import type { ExtraAppendLayerArg } from './visualization'; import { SeriesType, XYState, visualizationTypes } from './types'; -import { isHorizontalChart, isHorizontalSeries } from './state_helpers'; +import { isHorizontalChart, isHorizontalSeries, isPercentageSeries } from './state_helpers'; import { getDataLayers } from './visualization_helpers'; import { ExperimentalBadge } from '../../shared_components'; +import { ChartOption } from '../../editor_frame_service/editor_frame/config_panel/chart_switch/chart_option'; interface AddLayerButtonProps { state: XYState; @@ -36,7 +41,7 @@ interface AddLayerButtonProps { export enum AddLayerPanelType { main = 'main', selectAnnotationMethod = 'selectAnnotationMethod', - selectVisualizationType = 'selectVisualizationType', + compatibleVisualizationTypes = 'compatibleVisualizationTypes', } export function AddLayerButton({ @@ -63,11 +68,11 @@ export function AddLayerButton({ disabled, name: ( - + {label} - + ), @@ -85,7 +90,7 @@ export function AddLayerButton({ toolTipContent, }: (typeof supportedLayers)[0]) => { return { - panel: AddLayerPanelType.selectVisualizationType, + panel: AddLayerPanelType.compatibleVisualizationTypes, toolTipContent, disabled, name: {label}, @@ -101,9 +106,11 @@ export function AddLayerButton({ (t) => isHorizontalSeries(t.id as SeriesType) === horizontalOnly ); - const currentLayerVisType = + const currentLayerTypeIndex = availableVisTypes.findIndex((t) => t.id === getDataLayers(state.layers)?.[0]?.seriesType) || 0; + const firstLayerSubtype = getDataLayers(state.layers)?.[0]?.seriesType; + return ( <> toggleLayersChoice(!showLayersChoice)} iconType="layers" > @@ -145,6 +153,20 @@ export function AddLayerButton({ if (type === LayerTypes.ANNOTATIONS) { return annotationPanel(props); } else if (type === LayerTypes.DATA) { + if (horizontalOnly) { + return { + toolTipContent, + disabled, + name: {label}, + className: 'lnsLayerAddButton', + icon: icon && , + ['data-test-subj']: `lnsLayerAddButton-${type}`, + onClick: () => { + addLayer(type); + toggleLayersChoice(false); + }, + }; + } return dataPanel(props); } return { @@ -193,20 +215,40 @@ export function AddLayerButton({ ], }, { - id: AddLayerPanelType.selectVisualizationType, - initialFocusedItemIndex: currentLayerVisType, - title: i18n.translate('xpack.lens.layerPanel.selectVisualizationType', { - defaultMessage: 'Select visualization type', + id: AddLayerPanelType.compatibleVisualizationTypes, + initialFocusedItemIndex: currentLayerTypeIndex, + title: i18n.translate('xpack.lens.layerPanel.compatibleVisualizationTypes', { + defaultMessage: 'Compatible visualization types', + }), + width: 340, + items: availableVisTypes.map((t) => { + const canInitializeWithSubtype = + t.subtypes?.includes(firstLayerSubtype) && !isPercentageSeries(firstLayerSubtype); + + return { + renderItem: () => { + return ( + { + addLayer( + LayerTypes.DATA, + undefined, + undefined, + canInitializeWithSubtype + ? firstLayerSubtype + : (t.subtypes?.[0] as SeriesType) + ); + toggleLayersChoice(false); + }} + /> + ); + }, + }; }), - items: availableVisTypes.map((t) => ({ - name: t.fullLabel || t.label, - icon: t.icon && , - onClick: () => { - addLayer(LayerTypes.DATA, undefined, undefined, t.id as SeriesType); - toggleLayersChoice(false); - }, - 'data-test-subj': `lnsXY_seriesType-${t.id}`, - })), }, ]} /> @@ -225,3 +267,41 @@ export function AddLayerButton({ ); } + +const ChartOptionWrapper = ({ + label, + description, + icon, + onClick, + type, +}: { + label: string; + description: string; + icon: IconType; + onClick: () => void; + type: string; +}) => { + return ( + + ); +}; diff --git a/x-pack/plugins/lens/public/visualizations/xy/state_helpers.tsx b/x-pack/plugins/lens/public/visualizations/xy/state_helpers.tsx index 85cc732373d52..07637c52b8c8d 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/state_helpers.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/state_helpers.tsx @@ -18,7 +18,7 @@ import { validateQuery } from '@kbn/visualization-ui-components'; import { DataViewsState } from '../../state_management'; import { FramePublicAPI, DatasourcePublicAPI, UserMessage } from '../../types'; import { - visualizationTypes, + visualizationSubtypes, XYLayerConfig, XYDataLayerConfig, XYReferenceLineLayerConfig, @@ -48,6 +48,26 @@ export function isHorizontalSeries(seriesType: SeriesType) { ); } +export function flipSeriesType(seriesType: SeriesType) { + switch (seriesType) { + case 'bar': + return 'bar_horizontal'; + case 'bar_stacked': + return 'bar_horizontal_stacked'; + case 'bar_percentage_stacked': + return 'bar_horizontal_percentage_stacked'; + case 'bar_horizontal': + return 'bar'; + case 'bar_horizontal_stacked': + return 'bar_stacked'; + case 'bar_horizontal_percentage_stacked': + return 'bar_percentage_stacked'; + + default: + return 'bar_horizontal'; + } +} + export function isPercentageSeries(seriesType: SeriesType) { return ( seriesType === 'bar_percentage_stacked' || @@ -56,16 +76,52 @@ export function isPercentageSeries(seriesType: SeriesType) { ); } +export const AREA_SERIES = ['area_stacked', 'area', 'area_percentage_stacked']; +export const NON_BAR_SERIES = [...AREA_SERIES, 'line']; +export const BAR_SERIES = [ + 'bar', + 'bar_stacked', + 'bar_percentage_stacked', + 'bar_horizontal', + 'bar_horizontal_stacked', + 'bar_horizontal_percentage_stacked', +]; + +export const hasNonBarSeries = (layers: XYLayerConfig[]) => + layers.some((layer) => isDataLayer(layer) && NON_BAR_SERIES.includes(layer.seriesType)); + +export const hasBarSeries = (layers: XYLayerConfig[]) => { + return layers.some((layer) => isDataLayer(layer) && BAR_SERIES.includes(layer.seriesType)); +}; + +export const hasAreaSeries = (layers: XYLayerConfig[]) => + layers.some((layer) => isDataLayer(layer) && AREA_SERIES.includes(layer.seriesType)); + +export const getBarSeriesLayers = (layers: XYLayerConfig[]): XYDataLayerConfig[] => + getDataLayers(layers).filter((layer) => BAR_SERIES.includes(layer.seriesType)); + export function isStackedChart(seriesType: SeriesType) { return seriesType.includes('stacked'); } +export const isAreaLayer = (layer: XYLayerConfig) => { + return isDataLayer(layer) && AREA_SERIES.includes(layer.seriesType); +}; + +export function isBarLayer(layer: XYLayerConfig) { + return isDataLayer(layer) && BAR_SERIES.includes(layer.seriesType); +} + +export const getUniqueSeriesTypes = (layers: XYLayerConfig[]) => { + return [...new Set(getDataLayers(layers).map(({ seriesType }) => seriesType))]; +}; + export function isHorizontalChart(layers: XYLayerConfig[]) { return getDataLayers(layers).every((l) => isHorizontalSeries(l.seriesType)); } export function getIconForSeries(type: SeriesType): EuiIconType { - const definition = visualizationTypes.find((t) => t.id === type); + const definition = visualizationSubtypes.find((t) => t.id === type); if (!definition) { throw new Error(`Unknown series type ${type}`); diff --git a/x-pack/plugins/lens/public/visualizations/xy/types.ts b/x-pack/plugins/lens/public/visualizations/xy/types.ts index 34d266c77f965..2ac9b3df7fdd2 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/types.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/types.ts @@ -179,23 +179,28 @@ export interface XYState { export type State = XYState; -const groupLabelForBar = i18n.translate('xpack.lens.xyVisualization.barGroupLabel', { - defaultMessage: 'Bar', -}); +const barShared = { + sortPriority: 1, + description: i18n.translate('xpack.lens.bar.visualizationDescription', { + defaultMessage: 'Compare categories or groups of data with bars.', + }), +}; -const groupLabelForLineAndArea = i18n.translate('xpack.lens.xyVisualization.lineGroupLabel', { - defaultMessage: 'Line and area', -}); +const areaShared = { + sortPriority: 3, + description: i18n.translate('xpack.lens.area.visualizationDescription', { + defaultMessage: 'Compare distributions of cumulative data trends.', + }), +}; -export const visualizationTypes: VisualizationType[] = [ +export const visualizationSubtypes: VisualizationType[] = [ { id: 'bar', icon: IconChartBar, label: i18n.translate('xpack.lens.xyVisualization.barLabel', { defaultMessage: 'Bar vertical', }), - groupLabel: groupLabelForBar, - sortPriority: 4, + ...barShared, }, { id: 'bar_horizontal', @@ -206,7 +211,7 @@ export const visualizationTypes: VisualizationType[] = [ fullLabel: i18n.translate('xpack.lens.xyVisualization.barHorizontalFullLabel', { defaultMessage: 'Bar horizontal', }), - groupLabel: groupLabelForBar, + ...barShared, }, { id: 'bar_stacked', @@ -214,7 +219,7 @@ export const visualizationTypes: VisualizationType[] = [ label: i18n.translate('xpack.lens.xyVisualization.stackedBarLabel', { defaultMessage: 'Bar vertical stacked', }), - groupLabel: groupLabelForBar, + ...barShared, }, { id: 'bar_percentage_stacked', @@ -222,7 +227,7 @@ export const visualizationTypes: VisualizationType[] = [ label: i18n.translate('xpack.lens.xyVisualization.stackedPercentageBarLabel', { defaultMessage: 'Bar vertical percentage', }), - groupLabel: groupLabelForBar, + ...barShared, }, { id: 'bar_horizontal_stacked', @@ -233,7 +238,7 @@ export const visualizationTypes: VisualizationType[] = [ fullLabel: i18n.translate('xpack.lens.xyVisualization.stackedBarHorizontalFullLabel', { defaultMessage: 'Bar horizontal stacked', }), - groupLabel: groupLabelForBar, + ...barShared, }, { id: 'bar_horizontal_percentage_stacked', @@ -247,7 +252,7 @@ export const visualizationTypes: VisualizationType[] = [ defaultMessage: 'Bar horizontal percentage', } ), - groupLabel: groupLabelForBar, + ...barShared, }, { id: 'area', @@ -255,7 +260,7 @@ export const visualizationTypes: VisualizationType[] = [ label: i18n.translate('xpack.lens.xyVisualization.areaLabel', { defaultMessage: 'Area', }), - groupLabel: groupLabelForLineAndArea, + ...areaShared, }, { id: 'area_stacked', @@ -263,7 +268,7 @@ export const visualizationTypes: VisualizationType[] = [ label: i18n.translate('xpack.lens.xyVisualization.stackedAreaLabel', { defaultMessage: 'Area stacked', }), - groupLabel: groupLabelForLineAndArea, + ...areaShared, }, { id: 'area_percentage_stacked', @@ -271,7 +276,7 @@ export const visualizationTypes: VisualizationType[] = [ label: i18n.translate('xpack.lens.xyVisualization.stackedPercentageAreaLabel', { defaultMessage: 'Area percentage', }), - groupLabel: groupLabelForLineAndArea, + ...areaShared, }, { id: 'line', @@ -279,7 +284,73 @@ export const visualizationTypes: VisualizationType[] = [ label: i18n.translate('xpack.lens.xyVisualization.lineLabel', { defaultMessage: 'Line', }), - groupLabel: groupLabelForLineAndArea, sortPriority: 2, + description: i18n.translate('xpack.lens.line.visualizationDescription', { + defaultMessage: 'Reveal variations in data over time.', + }), + }, +]; + +export const visualizationTypes: VisualizationType[] = [ + { + id: 'bar', + subtypes: [ + 'bar', + 'bar_stacked', + 'bar_percentage_stacked', + 'bar_horizontal', + 'bar_horizontal_stacked', + 'bar_horizontal_percentage_stacked', + ], + icon: IconChartBar, + label: i18n.translate('xpack.lens.xyVisualization.barLabel', { + defaultMessage: 'Bar', + }), + sortPriority: 1, + description: i18n.translate('xpack.lens.bar.visualizationDescription', { + defaultMessage: 'Compare categories or groups of data via bars.', + }), + getCompatibleSubtype: (seriesType?: string) => { + if (seriesType === 'area') { + return 'bar'; + } else if (seriesType === 'area_stacked') { + return 'bar_stacked'; + } else if (seriesType === 'area_percentage_stacked') { + return 'bar_percentage_stacked'; + } + }, + }, + { + id: 'area', + icon: IconChartArea, + label: i18n.translate('xpack.lens.xyVisualization.areaLabel', { + defaultMessage: 'Area', + }), + sortPriority: 3, + description: i18n.translate('xpack.lens.area.visualizationDescription', { + defaultMessage: 'Compare distributions of cumulative data trends.', + }), + subtypes: ['area', 'area_stacked', 'area_percentage_stacked'], + getCompatibleSubtype: (seriesType?: string) => { + if (seriesType === 'bar') { + return 'area'; + } else if (seriesType === 'bar_stacked') { + return 'area_stacked'; + } else if (seriesType === 'bar_percentage_stacked') { + return 'area_percentage_stacked'; + } + }, + }, + { + id: 'line', + icon: IconChartLine, + label: i18n.translate('xpack.lens.xyVisualization.lineLabel', { + defaultMessage: 'Line', + }), + sortPriority: 2, + description: i18n.translate('xpack.lens.line.visualizationDescription', { + defaultMessage: 'Reveal variations in data over time or categorically.', + }), + subtypes: ['line'], }, ]; diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.tsx index 03a391d4e29d6..012823831b8eb 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.tsx @@ -143,7 +143,7 @@ describe('xy_visualization', () => { const desc = xyVisualization.getDescription(mixedState()); expect(desc.icon).toEqual(IconChartBar); - expect(desc.label).toEqual('Bar vertical'); + expect(desc.label).toEqual('Bar'); }); it('should show mixed horizontal bar chart when multiple horizontal bar types', () => { @@ -156,18 +156,15 @@ describe('xy_visualization', () => { it('should show bar chart when bar only', () => { const desc = xyVisualization.getDescription(mixedState('bar_horizontal', 'bar_horizontal')); - - expect(desc.label).toEqual('Bar horizontal'); + expect(desc.label).toEqual('Bar'); }); it('should show the chart description if not mixed', () => { expect(xyVisualization.getDescription(mixedState('area')).label).toEqual('Area'); expect(xyVisualization.getDescription(mixedState('line')).label).toEqual('Line'); - expect(xyVisualization.getDescription(mixedState('area_stacked')).label).toEqual( - 'Area stacked' - ); + expect(xyVisualization.getDescription(mixedState('area_stacked')).label).toEqual('Area'); expect(xyVisualization.getDescription(mixedState('bar_horizontal_stacked')).label).toEqual( - 'Bar horizontal stacked' + 'Bar' ); }); }); @@ -196,17 +193,15 @@ describe('xy_visualization', () => { it('should combine multiple layers into one type', () => { expect( xyVisualization.getVisualizationTypeId(mixedState('bar_horizontal', 'bar_horizontal')) - ).toEqual('bar_horizontal'); + ).toEqual('bar'); }); it('should return the subtype for single layers', () => { expect(xyVisualization.getVisualizationTypeId(mixedState('area'))).toEqual('area'); expect(xyVisualization.getVisualizationTypeId(mixedState('line'))).toEqual('line'); - expect(xyVisualization.getVisualizationTypeId(mixedState('area_stacked'))).toEqual( - 'area_stacked' - ); + expect(xyVisualization.getVisualizationTypeId(mixedState('area_stacked'))).toEqual('area'); expect(xyVisualization.getVisualizationTypeId(mixedState('bar_horizontal_stacked'))).toEqual( - 'bar_horizontal_stacked' + 'bar' ); }); }); diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index 81eebf770485f..64a2ad4fc2754 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React from 'react'; +import React, { useState } from 'react'; import { Position } from '@elastic/charts'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; @@ -27,6 +27,8 @@ import { type AccessorConfig, DimensionTrigger } from '@kbn/visualization-ui-com import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import { getColorsFromMapping } from '@kbn/coloring'; import useObservable from 'react-use/lib/useObservable'; +import { EuiPopover, EuiSelectable } from '@elastic/eui'; +import { ToolbarButton } from '@kbn/shared-ux-button-toolbar'; import { generateId } from '../../id_generator'; import { isDraggedDataViewField, @@ -37,7 +39,7 @@ import { getColorMappingDefaults, } from '../../utils'; import { getSuggestions } from './xy_suggestions'; -import { XyToolbar } from './xy_config_panel'; +import { XyToolbar, updateLayer } from './xy_config_panel'; import { DataDimensionEditor, DataDimensionEditorDataSectionExtra, @@ -60,6 +62,7 @@ import { type XYLayerConfig, type XYDataLayerConfig, type SeriesType, + visualizationSubtypes, visualizationTypes, } from './types'; import { @@ -161,12 +164,13 @@ export const getXyVisualization = ({ savedObjectsTagging?: SavedObjectTaggingPluginStart; }): Visualization => ({ id: XY_ID, - visualizationTypes, - getVisualizationTypeId(state) { - const type = getVisualizationType(state); + getVisualizationTypeId(state, layerId) { + const type = getVisualizationType(state, layerId); return type === 'mixed' ? type : type.id; }, + visualizationTypes, + getLayerIds(state) { return getLayersByType(state).map((l) => l.layerId); }, @@ -259,14 +263,30 @@ export const getXyVisualization = ({ getDescription, switchVisualizationType(seriesType: string, state: State, layerId?: string) { + const dataLayer = state.layers.find((l) => l.layerId === layerId); + if (dataLayer && !isDataLayer(dataLayer)) { + throw new Error('Cannot switch series type for non-data layer'); + } + if (!dataLayer) { + return state; + } + // todo: test how they switch between percentage etc + const currentStackingType = stackingTypes.find(({ subtypes }) => + subtypes.includes(dataLayer.seriesType) + ); + const chosenTypeIndex = defaultSeriesTypesByIndex.indexOf(seriesType); + + const compatibleSeriesType: SeriesType = (currentStackingType?.subtypes[chosenTypeIndex] || + seriesType) as SeriesType; + return { ...state, - preferredSeriesType: seriesType as SeriesType, + preferredSeriesType: compatibleSeriesType, layers: layerId ? state.layers.map((layer) => - layer.layerId === layerId ? { ...layer, seriesType: seriesType as SeriesType } : layer + layer.layerId === layerId ? { ...layer, seriesType: compatibleSeriesType } : layer ) - : state.layers.map((layer) => ({ ...layer, seriesType: seriesType as SeriesType })), + : state.layers.map((layer) => ({ ...layer, seriesType: compatibleSeriesType })), }; }, @@ -672,6 +692,23 @@ export const getXyVisualization = ({ (!isHorizontalSeries(subtype1 as SeriesType) && !isHorizontalSeries(subtype2 as SeriesType)) ); }, + getSubtypeSwitch({ state, setState, layerId }) { + const index = state.layers.findIndex((l) => l.layerId === layerId); + const layer = state.layers[index]; + + if (!layer || !isDataLayer(layer) || layer.seriesType === 'line') { + return null; + } + + return () => ( + + setState(updateLayer(state, newLayer, index)) + } + /> + ); + }, getCustomLayerHeader(props) { const layer = props.state.layers.find((l) => l.layerId === props.layerId); @@ -1137,9 +1174,9 @@ function getVisualizationInfo( if (isDataLayer(layer)) { chartType = layer.seriesType; - const layerVisType = visualizationTypes.find((visType) => visType.id === chartType); + const layerVisType = visualizationSubtypes.find((visType) => visType.id === chartType); icon = layerVisType?.icon; - label = layerVisType?.fullLabel || layerVisType?.label; + label = layerVisType?.label; if (layer.xAccessor) { dimensions.push({ @@ -1295,3 +1332,97 @@ function getNotifiableFeatures( }, ]; } + +const defaultSeriesTypesByIndex = ['bar', 'area', 'bar_horizontal']; + +export const stackingTypes = [ + { + type: 'stacked', + label: i18n.translate('xpack.lens.shared.barLayerStacking.stacked', { + defaultMessage: 'Stacked', + }), + subtypes: ['bar_stacked', 'area_stacked', 'bar_horizontal_stacked'], + }, + { + type: 'unstacked', + label: i18n.translate('xpack.lens.shared.barLayerStacking.unstacked', { + defaultMessage: 'Unstacked', + }), + subtypes: ['bar', 'area', 'bar_horizontal'], + }, + { + type: 'percentage', + label: i18n.translate('xpack.lens.shared.barLayerStacking.percentage', { + defaultMessage: 'Percentage', + }), + subtypes: [ + 'bar_percentage_stacked', + 'area_percentage_stacked', + 'bar_horizontal_percentage_stacked', + ], + }, +]; + +const SubtypeSwitch = ({ + layer, + setLayerState, +}: { + layer: XYDataLayerConfig; + setLayerState: (l: XYDataLayerConfig) => void; +}): JSX.Element | null => { + const [flyoutOpen, setFlyoutOpen] = useState(false); + + const stackingType = stackingTypes.find(({ subtypes }) => subtypes.includes(layer.seriesType)); + if (!stackingType) { + return null; + } + const subTypeIndex = stackingType.subtypes.indexOf(layer.seriesType); + const options = stackingTypes.map(({ label, subtypes }) => ({ + label, + value: subtypes[subTypeIndex], + checked: subtypes[subTypeIndex] === layer.seriesType ? ('on' as const) : undefined, + })); + + return ( + <> + setFlyoutOpen(true)} + fullWidth + size="s" + label={stackingType.label} + /> + } + isOpen={flyoutOpen} + closePopover={() => setFlyoutOpen(false)} + anchorPosition="downLeft" + > + { + setFlyoutOpen(false); + const chosenType = newOptions.find(({ checked }) => checked === 'on'); + if (!chosenType) { + return; + } + setLayerState({ + ...layer, + seriesType: chosenType.value as SeriesType, + }); + }} + > + {(list) => list} + + + + ); +}; diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx index e627df3883391..970671128aecf 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx @@ -18,7 +18,6 @@ import { } from '../../types'; import { State, - visualizationTypes, XYState, XYAnnotationLayerConfig, XYLayerConfig, @@ -27,6 +26,8 @@ import { SeriesType, XYByReferenceAnnotationLayerConfig, XYByValueAnnotationLayerConfig, + visualizationTypes, + visualizationSubtypes, } from './types'; import { isHorizontalChart } from './state_helpers'; import { layerTypes } from '../..'; @@ -191,10 +192,27 @@ export const getLayerTypeOptions = (layer: XYLayerConfig, options: LayerTypeToLa return options[layerTypes.ANNOTATIONS](layer); }; +export function getVisualizationSubtypeId(state: State) { + if (!state.layers.length) { + return ( + visualizationSubtypes.find((t) => t.id === state.preferredSeriesType) ?? + visualizationSubtypes[0] + ).id; + } + const dataLayers = getDataLayers(state?.layers); + const subtype = ( + visualizationSubtypes.find((t) => t.id === dataLayers[0].seriesType) ?? visualizationSubtypes[0] + ).id; + const seriesTypes = uniq(dataLayers.map((l) => l.seriesType)); + + return subtype && seriesTypes.length === 1 ? subtype : 'mixed'; +} + export function getVisualizationType(state: State, layerId?: string): VisualizationType | 'mixed' { if (!state.layers.length) { return ( - visualizationTypes.find((t) => t.id === state.preferredSeriesType) ?? visualizationTypes[0] + visualizationTypes.find((t) => t.subtypes?.includes(state.preferredSeriesType)) ?? + visualizationTypes[0] ); } const dataLayers = getDataLayers(state?.layers); @@ -202,9 +220,14 @@ export function getVisualizationType(state: State, layerId?: string): Visualizat const dataLayerSeries = layerId ? dataLayers.find((d) => d.layerId === layerId)?.seriesType : dataLayers[0].seriesType; - return visualizationTypes.find((t) => t.id === dataLayerSeries) || 'mixed'; + return ( + visualizationTypes.find((t) => dataLayerSeries && t.subtypes?.includes(dataLayerSeries)) || + visualizationTypes[0] + ); } - const visualizationType = visualizationTypes.find((t) => t.id === dataLayers[0].seriesType); + const visualizationType = + visualizationTypes.find((t) => t.subtypes?.includes(dataLayers[0].seriesType)) ?? + visualizationTypes[0]; const seriesTypes = uniq(dataLayers.map((l) => l.seriesType)); return visualizationType && seriesTypes.length === 1 ? visualizationType : 'mixed'; diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.test.tsx new file mode 100644 index 0000000000000..3d675883ec6bf --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.test.tsx @@ -0,0 +1,269 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { mountWithIntl as mount } from '@kbn/test-jest-helpers'; +import { EuiButtonGroupProps, EuiButtonGroup } from '@elastic/eui'; +import { DataDimensionEditor } from './dimension_editor'; +import { FramePublicAPI, DatasourcePublicAPI } from '../../../types'; +import { State, XYState, XYDataLayerConfig } from '../types'; +import { Position } from '@elastic/charts'; +import { createMockFramePublicAPI, createMockDatasource } from '../../../mocks'; +import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; +import { EuiColorPicker } from '@elastic/eui'; +import { LayerTypes } from '@kbn/expression-xy-plugin/public'; +import { act } from 'react-dom/test-utils'; + +describe('XY Config panels', () => { + let frame: FramePublicAPI; + + function testState(): State { + return { + legend: { isVisible: true, position: Position.Right }, + valueLabels: 'hide', + preferredSeriesType: 'bar', + layers: [ + { + seriesType: 'bar', + layerType: LayerTypes.DATA, + layerId: 'first', + splitAccessor: 'baz', + xAccessor: 'foo', + accessors: ['bar'], + }, + ], + }; + } + + beforeEach(() => { + frame = createMockFramePublicAPI(); + frame.datasourceLayers = { + first: createMockDatasource('test').publicAPIMock, + }; + }); + + describe('Dimension Editor', () => { + test('shows the correct axis side options when in horizontal mode', () => { + const state = testState(); + const component = mount( + + ); + + const options = component + .find(EuiButtonGroup) + .first() + .prop('options') as EuiButtonGroupProps['options']; + + expect(options!.map(({ label }) => label)).toEqual(['Bottom', 'Auto', 'Top']); + }); + + test('shows the default axis side options when not in horizontal mode', () => { + const state = testState(); + const component = mount( + + ); + + const options = component + .find(EuiButtonGroup) + .first() + .prop('options') as EuiButtonGroupProps['options']; + + expect(options!.map(({ label }) => label)).toEqual(['Left', 'Auto', 'Right']); + }); + + test('sets the color of a dimension to the color from palette service if not set explicitly', () => { + const state = { + ...testState(), + layers: [ + { + seriesType: 'bar', + layerType: LayerTypes.DATA, + layerId: 'first', + splitAccessor: undefined, + xAccessor: 'foo', + accessors: ['bar'], + }, + ], + } as XYState; + const component = mount( + + ); + + expect(component.find(EuiColorPicker).prop('color')).toEqual('black'); + }); + + test('uses the overwrite color if set', () => { + const state = { + ...testState(), + layers: [ + { + seriesType: 'bar', + layerType: LayerTypes.DATA, + layerId: 'first', + splitAccessor: undefined, + xAccessor: 'foo', + accessors: ['bar'], + yConfig: [{ forAccessor: 'bar', color: 'red' }], + }, + ], + } as XYState; + + const component = mount( + + ); + + expect(component.find(EuiColorPicker).prop('color')).toEqual('red'); + }); + test('does not apply incorrect color', () => { + jest.useFakeTimers(); + const setState = jest.fn(); + const state = { + ...testState(), + layers: [ + { + seriesType: 'bar', + layerType: LayerTypes.DATA, + layerId: 'first', + splitAccessor: undefined, + xAccessor: 'foo', + accessors: ['bar'], + yConfig: [{ forAccessor: 'bar', color: 'red' }], + }, + ], + } as XYState; + + const component = mount( + + ); + + act(() => { + component + .find('input[data-test-subj="euiColorPickerAnchor indexPattern-dimension-colorPicker"]') + .simulate('change', { + target: { value: 'INCORRECT_COLOR' }, + }); + }); + component.update(); + jest.advanceTimersByTime(256); + expect(component.find(EuiColorPicker).prop('color')).toEqual('INCORRECT_COLOR'); + expect(setState).not.toHaveBeenCalled(); + + act(() => { + component + .find('input[data-test-subj="euiColorPickerAnchor indexPattern-dimension-colorPicker"]') + .simulate('change', { + target: { value: '666666' }, + }); + }); + component.update(); + jest.advanceTimersByTime(256); + expect(component.find(EuiColorPicker).prop('color')).toEqual('666666'); + expect(setState).toHaveBeenCalled(); + jest.useRealTimers(); + }); + }); +}); diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/index.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/index.tsx index fca945f469dee..40eefb82eb611 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/index.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/index.tsx @@ -15,11 +15,12 @@ import { LegendSize, XYLegendValue } from '@kbn/visualizations-plugin/common/con import type { LegendSettingsPopoverProps } from '../../../shared_components/legend/legend_settings_popover'; import type { VisualizationToolbarProps, FramePublicAPI } from '../../../types'; import { State, XYState, AxesSettingsConfig } from '../types'; -import { isHorizontalChart } from '../state_helpers'; +import { hasBarSeries, isHorizontalChart } from '../state_helpers'; import { hasNumericHistogramDimension, LegendSettingsPopover } from '../../../shared_components'; import { AxisSettingsPopover } from './axis_settings_popover'; import { getAxesConfiguration, getXDomain, AxisGroupConfiguration } from '../axes_configuration'; import { VisualOptionsPopover } from './visual_options_popover'; +import { TextPopover } from './titles_and_text_popover'; import { getScaleType } from '../to_expression'; import { getDefaultVisualValuesForLayer } from '../../../shared_components/datasource_default_values'; import { getDataLayers } from '../visualization_helpers'; @@ -502,159 +503,25 @@ export const XyToolbar = memo(function XyToolbar( ).truncateText; const legendSize = state.legend.legendSize; + return ( - - + + {hasBarSeries(state.layers) && ( + + - - { - setState({ - ...state, - legend: { - ...state.legend, - isInside: location === 'inside', - }, - }); - }} - titlePlaceholder={ - frame.activeData?.[dataLayers[0].layerId]?.columns.find( - (col) => col.id === dataLayers[0].splitAccessor - )?.name ?? defaultLegendTitle - } - legendTitle={state?.legend.title} - onLegendTitleChange={({ title, visible }) => { - setState({ - ...state, - legend: { - ...state.legend, - title, - isTitleVisible: visible, - }, - }); - }} - isTitleVisible={state?.legend.isTitleVisible} - onDisplayChange={(optionId) => { - const newMode = legendOptions.find(({ id }) => id === optionId)!.value; - if (newMode === 'auto') { - setState({ - ...state, - legend: { - ...state.legend, - isVisible: true, - showSingleSeries: false, - }, - }); - } else if (newMode === 'show') { - setState({ - ...state, - legend: { - ...state.legend, - isVisible: true, - showSingleSeries: true, - }, - }); - } else if (newMode === 'hide') { - setState({ - ...state, - legend: { - ...state.legend, - isVisible: false, - showSingleSeries: false, - }, - }); - } - }} - position={state?.legend.position} - horizontalAlignment={state?.legend.horizontalAlignment} - verticalAlignment={state?.legend.verticalAlignment} - floatingColumns={state?.legend.floatingColumns} - onFloatingColumnsChange={(val) => { - setState({ - ...state, - legend: { ...state.legend, floatingColumns: val }, - }); - }} - maxLines={state?.legend.maxLines} - onMaxLinesChange={(val) => { - setState({ - ...state, - legend: { ...state.legend, maxLines: val }, - }); - }} - shouldTruncate={state?.legend.shouldTruncate ?? defaultParamsFromDatasources} - onTruncateLegendChange={() => { - const current = state?.legend.shouldTruncate ?? defaultParamsFromDatasources; - setState({ - ...state, - legend: { ...state.legend, shouldTruncate: !current }, - }); - }} - onPositionChange={(id) => { - setState({ - ...state, - legend: { ...state.legend, position: id as Position }, - }); - }} - onAlignmentChange={(value) => { - const [vertical, horizontal] = value.split('_'); - const verticalAlignment = vertical as LegendSettingsPopoverProps['verticalAlignment']; - const horizontalAlignment = - horizontal as LegendSettingsPopoverProps['horizontalAlignment']; - - setState({ - ...state, - legend: { ...state.legend, verticalAlignment, horizontalAlignment }, - }); - }} - allowedLegendStats={nonOrdinalXAxis ? xyLegendValues : undefined} - legendStats={state?.legend.legendStats} - onLegendStatsChange={(legendStats, hasConvertedToTable) => { - if (hasConvertedToTable) { - setState({ - ...state, - legend: { - ...state.legend, - legendStats, - legendSize: LegendSize.AUTO, - isVisible: true, - showSingleSeries: true, - }, - }); - return; - } - setState({ - ...state, - legend: { - ...state.legend, - legendStats, - isVisible: true, - showSingleSeries: true, - }, - }); - }} - legendSize={legendSize} - onLegendSizeChange={(newLegendSize) => { - setState({ - ...state, - legend: { - ...state.legend, - legendSize: newLegendSize, - }, - }); - }} - showAutoLegendSizeOption={true} - /> - - + + )} @@ -766,6 +633,118 @@ export const XyToolbar = memo(function XyToolbar( + + + { + setState({ + ...state, + legend: { + ...state.legend, + isInside: location === 'inside', + }, + }); + }} + titlePlaceholder={ + frame.activeData?.[dataLayers[0].layerId]?.columns.find( + (col) => col.id === dataLayers[0].splitAccessor + )?.name ?? defaultLegendTitle + } + legendTitle={state?.legend.title} + onLegendTitleChange={({ title, visible }) => { + setState({ + ...state, + legend: { + ...state.legend, + title, + isTitleVisible: visible, + }, + }); + }} + isTitleVisible={state?.legend.isTitleVisible} + onDisplayChange={(optionId) => { + const newMode = legendOptions.find(({ id }) => id === optionId)!.value; + setState({ + ...state, + legend: { + ...state.legend, + isVisible: newMode !== 'hide', + showSingleSeries: newMode === 'show', + }, + }); + }} + position={state?.legend.position} + horizontalAlignment={state?.legend.horizontalAlignment} + verticalAlignment={state?.legend.verticalAlignment} + floatingColumns={state?.legend.floatingColumns} + onFloatingColumnsChange={(val) => { + setState({ + ...state, + legend: { ...state.legend, floatingColumns: val }, + }); + }} + maxLines={state?.legend.maxLines} + onMaxLinesChange={(val) => { + setState({ + ...state, + legend: { ...state.legend, maxLines: val }, + }); + }} + shouldTruncate={state?.legend.shouldTruncate ?? defaultParamsFromDatasources} + onTruncateLegendChange={() => { + const current = state?.legend.shouldTruncate ?? defaultParamsFromDatasources; + setState({ + ...state, + legend: { ...state.legend, shouldTruncate: !current }, + }); + }} + onPositionChange={(id) => { + setState({ + ...state, + legend: { ...state.legend, position: id as Position }, + }); + }} + onAlignmentChange={(value) => { + const [vertical, horizontal] = value.split('_'); + const verticalAlignment = vertical as LegendSettingsPopoverProps['verticalAlignment']; + const horizontalAlignment = + horizontal as LegendSettingsPopoverProps['horizontalAlignment']; + + setState({ + ...state, + legend: { ...state.legend, verticalAlignment, horizontalAlignment }, + }); + }} + allowedLegendStats={nonOrdinalXAxis ? xyLegendValues : undefined} + legendStats={state?.legend.legendStats} + onLegendStatsChange={(legendStats, hasConvertedToTable) => { + setState({ + ...state, + legend: { + ...state.legend, + legendStats, + isVisible: true, + showSingleSeries: true, + ...(hasConvertedToTable ? { legendSize: LegendSize.AUTO } : {}), + }, + }); + }} + legendSize={legendSize} + onLegendSizeChange={(newLegendSize) => { + setState({ + ...state, + legend: { + ...state.legend, + legendSize: newLegendSize, + }, + }); + }} + showAutoLegendSizeOption={true} + /> + ); }); diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/titles_and_text_popover/index.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/titles_and_text_popover/index.tsx new file mode 100644 index 0000000000000..09e89dbeed842 --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/titles_and_text_popover/index.tsx @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { ToolbarPopover, ValueLabelsSettings } from '../../../../shared_components'; +import { XYState } from '../../types'; +import type { FramePublicAPI } from '../../../../types'; + +export interface TextPopoverProps { + state: XYState; + setState: (newState: XYState) => void; + datasourceLayers: FramePublicAPI['datasourceLayers']; +} +const PANEL_STYLE = { + width: '460px', +}; + +export const TextPopover: React.FC = ({ state, setState }) => { + return ( + + { + setState({ ...state, valueLabels: newMode }); + }} + /> + + ); +}; diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/fill_opacity_option.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/fill_opacity_option.test.tsx index a6a30efa6139b..d5020333bd82c 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/fill_opacity_option.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/fill_opacity_option.test.tsx @@ -12,16 +12,16 @@ import { render, screen } from '@testing-library/react'; describe('Line curve option', () => { it('should show currently selected opacity value', () => { render(); - expect(screen.getByLabelText('Fill opacity')).toHaveValue(0.3); + expect(screen.getByLabelText('Area fill opacity')).toHaveValue(0.3); }); it('should show fill opacity option when enabled', () => { render(); - expect(screen.getByLabelText('Fill opacity')).toBeInTheDocument(); + expect(screen.getByLabelText('Area fill opacity')).toBeInTheDocument(); }); it('should hide curve option when disabled', () => { render(); - expect(screen.queryByLabelText('Fill opacity')).not.toBeInTheDocument(); + expect(screen.queryByLabelText('Area fill opacity')).not.toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/fill_opacity_option.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/fill_opacity_option.tsx index 8e71ace4f8bed..f493c5a95366e 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/fill_opacity_option.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/fill_opacity_option.tsx @@ -35,8 +35,8 @@ export const FillOpacityOption: React.FC = ({ <> diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/index.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/index.tsx index 5fd35a780cda5..fccd5f2ca172e 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/index.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/index.tsx @@ -8,12 +8,22 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { TooltipWrapper } from '@kbn/visualization-utils'; -import { ToolbarPopover, ValueLabelsSettings } from '../../../../shared_components'; +import { BarOrientationSettings } from '../../../../shared_components/bar_orientation'; +import { ToolbarDivider } from '../../../../shared_components/toolbar_divider'; +import { ToolbarPopover } from '../../../../shared_components'; import { MissingValuesOptions } from './missing_values_option'; import { LineCurveOption } from './line_curve_option'; import { FillOpacityOption } from './fill_opacity_option'; import { XYState } from '../../types'; -import { hasHistogramSeries } from '../../state_helpers'; +import { + flipSeriesType, + getBarSeriesLayers, + hasAreaSeries, + hasHistogramSeries, + hasNonBarSeries, + isBarLayer, + isHorizontalChart, +} from '../../state_helpers'; import type { FramePublicAPI } from '../../../../types'; import { getDataLayers } from '../../visualization_helpers'; @@ -39,6 +49,10 @@ function getValueLabelDisableReason({ }); } +const PANEL_STYLE = { + width: 500, +}; + export interface VisualOptionsPopoverProps { state: XYState; setState: (newState: XYState) => void; @@ -55,38 +69,78 @@ export const VisualOptionsPopover: React.FC = ({ ({ seriesType }) => seriesType === 'area_percentage_stacked' ); - const hasNonBarSeries = dataLayers.some(({ seriesType }) => - ['area_stacked', 'area', 'line', 'area_percentage_stacked'].includes(seriesType) - ); - - const hasAreaSeries = dataLayers.some(({ seriesType }) => - ['area_stacked', 'area', 'area_percentage_stacked'].includes(seriesType) - ); - + const isHasNonBarSeries = hasNonBarSeries(dataLayers); const isHistogramSeries = Boolean(hasHistogramSeries(dataLayers, datasourceLayers)); - const isValueLabelsEnabled = !hasNonBarSeries; - const isFittingEnabled = hasNonBarSeries && !isAreaPercentage; - const isCurveTypeEnabled = hasNonBarSeries || isAreaPercentage; + const isFittingEnabled = isHasNonBarSeries && !isAreaPercentage; + const isCurveTypeEnabled = isHasNonBarSeries || isAreaPercentage; const valueLabelsDisabledReason = getValueLabelDisableReason({ isAreaPercentage, isHistogramSeries, }); + const isHorizontal = isHorizontalChart(state.layers); - const isDisabled = !isValueLabelsEnabled && !isFittingEnabled && !isCurveTypeEnabled; + const isDisabled = !isFittingEnabled && !isCurveTypeEnabled && isHasNonBarSeries; + + const barSeriesLayers = getBarSeriesLayers(dataLayers); + + const hasAnyBarSetting = !!barSeriesLayers.length; + const hasAreaSettings = hasAreaSeries(dataLayers); + const shouldDisplayDividerHr = !!(hasAnyBarSetting && hasAreaSettings); return ( + {hasAnyBarSetting ? ( + { + const newSeriesType = flipSeriesType(dataLayers[0].seriesType); + setState({ + ...state, + layers: state.layers.map((layer) => + isBarLayer(layer) + ? { + ...layer, + seriesType: newSeriesType, + } + : layer + ), + }); + }} + /> + ) : null} + + {shouldDisplayDividerHr ? : null} + + {hasAreaSettings ? ( + <> + { + setState({ + ...state, + fillOpacity: newValue, + }); + }} + /> + + + + ) : null} = ({ }); }} /> - - { - setState({ ...state, valueLabels: newMode }); - }} - /> - = ({ setState({ ...state, endValue: newVal }); }} /> - - { - setState({ - ...state, - fillOpacity: newValue, - }); - }} - /> ); diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/line_curve_option.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/line_curve_option.tsx index a0bbda7d1f3ec..e78de552f3028 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/line_curve_option.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/line_curve_option.tsx @@ -29,6 +29,7 @@ export const LineCurveOption: React.FC = ({ label={i18n.translate('xpack.lens.xyChart.lineInterpolationLabel', { defaultMessage: 'Line interpolation', })} + fullWidth > = ({ {isFittingEnabled && ( <> {i18n.translate('xpack.lens.xyChart.missingValuesLabel', { diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx index a6e6d54893d66..c4be5efc8b287 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx @@ -6,16 +6,15 @@ */ import React from 'react'; -import { shallowWithIntl as shallow } from '@kbn/test-jest-helpers'; import { Position } from '@elastic/charts'; import type { FramePublicAPI } from '../../../../types'; import { createMockDatasource, createMockFramePublicAPI } from '../../../../mocks'; -import { State, XYLayerConfig } from '../../types'; -import { VisualOptionsPopover } from '.'; -import { ToolbarPopover, ValueLabelsSettings } from '../../../../shared_components'; -import { MissingValuesOptions } from './missing_values_option'; -import { FillOpacityOption } from './fill_opacity_option'; +import { SeriesType, State } from '../../types'; +import { VisualOptionsPopover, VisualOptionsPopoverProps } from '.'; import { LayerTypes } from '@kbn/expression-xy-plugin/public'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { XYDataLayerConfig } from '@kbn/visualizations-plugin/common'; describe('Visual options popover', () => { let frame: FramePublicAPI; @@ -45,170 +44,43 @@ describe('Visual options popover', () => { }; }); - it('should disable the values and fitting for percentage area charts', () => { + const renderVisualOptionsPopover = (overrideProps?: Partial) => { const state = testState(); - const component = shallow( + return render( ); - - expect(component.find(ValueLabelsSettings).prop('isVisible')).toEqual(false); - expect(component.find(MissingValuesOptions).prop('isFittingEnabled')).toEqual(false); - }); - - it('should not disable the fill opacity for percentage area charts', () => { - const state = testState(); - const component = shallow( - - ); - - expect(component.find(FillOpacityOption).prop('isFillOpacityEnabled')).toEqual(true); - }); - - it('should not disable the visual options for percentage area charts', () => { - const state = testState(); - const component = shallow( - - ); - - expect(component.find(ToolbarPopover).prop('isDisabled')).toEqual(false); - }); - - it('should hide the fitting option for bar series', () => { - const state = testState(); - const component = shallow( - - ); - - expect(component.find(MissingValuesOptions).prop('isFittingEnabled')).toEqual(false); - }); - - it('should hide the fill opacity option for bar series', () => { - const state = testState(); - const component = shallow( - - ); - - expect(component.find(FillOpacityOption).prop('isFillOpacityEnabled')).toEqual(false); - }); - - it('should hide the fill opacity option for line series', () => { - const state = testState(); - const component = shallow( - - ); - - expect(component.find(FillOpacityOption).prop('isFillOpacityEnabled')).toEqual(false); - }); - - it('should show the popover and display field enabled for bar and horizontal_bar series', () => { - const state = testState(); - - const component = shallow( - - ); - - expect(component.find(ValueLabelsSettings).prop('isVisible')).toEqual(true); - }); - - it('should hide in the popover the display option for area and line series', () => { - const state = testState(); - const component = shallow( - - ); - - expect(component.find(ValueLabelsSettings).prop('isVisible')).toEqual(false); - }); - - it('should keep the display option for bar series with multiple layers', () => { - frame.datasourceLayers = { - ...frame.datasourceLayers, - second: createMockDatasource('test').publicAPIMock, - }; - - const state = testState(); - const component = shallow( - - ); - - expect(component.find(ValueLabelsSettings).prop('isVisible')).toEqual(true); - }); + }; + + const openAppearancePopover = () => { + userEvent.click(screen.getByRole('button', { name: 'Appearance' })); + }; + + it.each<{ seriesType: string; showsMissingValues?: boolean; showsFillOpacity?: boolean }>([ + { seriesType: 'area_percentage_stacked', showsMissingValues: false, showsFillOpacity: true }, + { seriesType: 'bar_horizontal', showsMissingValues: false, showsFillOpacity: false }, + { seriesType: 'line', showsMissingValues: true, showsFillOpacity: false }, + ])( + `should show settings for seriesTypes: $seriesType`, + ({ seriesType, showsMissingValues = false, showsFillOpacity = false }) => { + const state = testState(); + (state.layers[0] as XYDataLayerConfig).seriesType = seriesType as SeriesType; + renderVisualOptionsPopover({ state }); + openAppearancePopover(); + if (showsMissingValues) { + expect(screen.getByText('Missing values')).toBeInTheDocument(); + } else { + expect(screen.queryByText('Missing values')).not.toBeInTheDocument(); + } + if (showsFillOpacity) { + expect(screen.getAllByTestId('lnsFillOpacity')).toHaveLength(2); + } else { + expect(screen.queryAllByTestId('lnsFillOpacity')).toHaveLength(0); + } + } + ); }); diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/xy_config_panel.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/xy_config_panel.test.tsx index b0f553969e058..f0a8dddfc358f 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/xy_config_panel.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/xy_config_panel.test.tsx @@ -6,30 +6,19 @@ */ import React from 'react'; -import { mountWithIntl as mount, shallowWithIntl as shallow } from '@kbn/test-jest-helpers'; -import { EuiButtonGroupProps, EuiButtonGroup } from '@elastic/eui'; +import { shallowWithIntl as shallow } from '@kbn/test-jest-helpers'; import { XyToolbar } from '.'; -import { DataDimensionEditor } from './dimension_editor'; import { AxisSettingsPopover } from './axis_settings_popover'; -import { FramePublicAPI, DatasourcePublicAPI } from '../../../types'; +import { FramePublicAPI, DatasourcePublicAPI, VisualizationToolbarProps } from '../../../types'; import { State, XYState, XYDataLayerConfig } from '../types'; import { Position } from '@elastic/charts'; import { createMockFramePublicAPI, createMockDatasource } from '../../../mocks'; -import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; -import { EuiColorPicker } from '@elastic/eui'; import { LayerTypes } from '@kbn/expression-xy-plugin/public'; -import { act } from 'react-dom/test-utils'; +import { fireEvent, render, screen, within } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { getSelectedButtonInGroup } from '@kbn/test-eui-helpers'; -jest.mock('lodash', () => { - const original = jest.requireActual('lodash'); - - return { - ...original, - debounce: (fn: unknown) => fn, - }; -}); - -describe('XY Config panels', () => { +describe('XY Toolbar', () => { let frame: FramePublicAPI; function testState(): State { @@ -44,7 +33,7 @@ describe('XY Config panels', () => { layerId: 'first', splitAccessor: 'baz', xAccessor: 'foo', - accessors: ['bar'], + accessors: ['one'], }, ], }; @@ -57,54 +46,98 @@ describe('XY Config panels', () => { }; }); - describe('XyToolbar', () => { - it('should disable the popover if there is no right axis', () => { - const state = testState(); - const component = shallow(); + const renderToolbar = ( + overrideProps?: Partial< + VisualizationToolbarProps & { + useLegacyTimeAxis?: boolean; + } + > + ) => { + const state = testState(); + const rtlRender = render( + + ); + return rtlRender; + }; - expect(component.find(AxisSettingsPopover).at(2).prop('isDisabled')).toEqual(true); + const getRightAxisButton = () => screen.getByRole('button', { name: 'Right axis' }); + const getLeftAxisButton = () => screen.getByRole('button', { name: 'Left axis' }); + const getBottomAxisButton = () => screen.getByRole('button', { name: 'Bottom axis' }); + const queryTitlesAndTextButton = () => screen.queryByRole('button', { name: 'Titles and text' }); + + describe('Titles and text settings', () => { + it.each<{ seriesType: string[]; disallowed?: boolean }>([ + { seriesType: ['bar'] }, + { seriesType: ['bar_horizontal'] }, + { seriesType: ['bar_horizontal', 'line', 'area'] }, + { seriesType: ['bar_horizontal', 'bar'] }, + { seriesType: ['area'], disallowed: true }, + { seriesType: ['line'], disallowed: true }, + { seriesType: ['line', 'area'], disallowed: true }, + ])( + `should show titles and text settings when seriesType is $seriesType when bar series exist`, + ({ seriesType, disallowed = false }) => { + const state = testState(); + seriesType.forEach((type, i) => { + state.layers[i] = { ...state.layers[0], seriesType: type } as XYDataLayerConfig; + }); + renderToolbar({ state }); + + if (disallowed) { + expect(queryTitlesAndTextButton()).not.toBeInTheDocument(); + } else { + expect(queryTitlesAndTextButton()).toBeInTheDocument(); + } + } + ); + }); + describe('Axis settings', () => { + it('should disable the popover if there is no right axis', () => { + renderToolbar(); + expect(screen.getByRole('button', { name: 'Right axis' })).toBeDisabled(); }); it('should enable the popover if there is right axis', () => { const state = testState(); - const component = shallow( - - ); + renderToolbar({ + state: { + ...state, + layers: [ + { + ...state.layers[0], + yConfig: [{ axisMode: 'right', forAccessor: 'one' }], + } as XYDataLayerConfig, + ], + }, + }); - expect(component.find(AxisSettingsPopover).at(2).prop('isDisabled')).toEqual(false); + expect(getRightAxisButton()).toBeEnabled(); }); - it('should render the AxisSettingsPopover 3 times', () => { + it('should render the settings for all 3 axes', () => { const state = testState(); - const component = shallow( - - ); + renderToolbar({ + state: { + ...state, + layers: [ + { + ...state.layers[0], + accessors: ['one', 'two'], + yConfig: [{ axisMode: 'right', forAccessor: 'bar' }], + } as XYDataLayerConfig, + ], + }, + }); - expect(component.find(AxisSettingsPopover).length).toEqual(3); + expect(getLeftAxisButton()).toBeInTheDocument(); + expect(getBottomAxisButton()).toBeEnabled(); + expect(getRightAxisButton()).toBeEnabled(); }); it('should pass in endzone visibility setter and current sate for time chart', () => { @@ -113,27 +146,33 @@ describe('XY Config panels', () => { dataType: 'date', }); const state = testState(); - const component = shallow( - - ); + renderToolbar({ + frame, + state: { + ...state, + layers: [ + { + ...state.layers[0], + accessors: ['one', 'two'], + yConfig: [{ axisMode: 'right', forAccessor: 'one' }], + } as XYDataLayerConfig, + ], + }, + }); + + userEvent.click(getRightAxisButton()); + expect( + within(screen.getByRole('dialog', { name: 'Right axis' })).queryByTestId('lnsshowEndzones') + ).not.toBeInTheDocument(); - expect(component.find(AxisSettingsPopover).at(0).prop('setEndzoneVisibility')).toBeFalsy(); - expect(component.find(AxisSettingsPopover).at(1).prop('setEndzoneVisibility')).toBeTruthy(); - expect(component.find(AxisSettingsPopover).at(1).prop('endzonesVisible')).toBe(false); - expect(component.find(AxisSettingsPopover).at(2).prop('setEndzoneVisibility')).toBeFalsy(); + userEvent.click(getBottomAxisButton()); + expect( + within(screen.getByRole('dialog', { name: 'Bottom axis' })).queryByTestId('lnsshowEndzones') + ).toBeInTheDocument(); + userEvent.click(getLeftAxisButton()); + expect( + within(screen.getByRole('dialog', { name: 'Left axis' })).queryByTestId('lnsshowEndzones') + ).not.toBeInTheDocument(); }); it('should pass in current time marker visibility setter and current state for time chart', () => { @@ -172,61 +211,57 @@ describe('XY Config panels', () => { frame.activeData = { first: { type: 'datatable', - rows: [{ bar: -5 }, { bar: 50 }], + rows: [{ one: -5 }, { one: 50 }], columns: [ { - id: 'baz', + id: 'one', meta: { type: 'number', }, - name: 'baz', - }, - { - id: 'foo', - meta: { - type: 'number', - }, - name: 'foo', - }, - { - id: 'bar', - meta: { - type: 'number', - }, - name: 'bar', + name: 'one', }, ], }, }; - const component = shallow( + + render( ); - - expect(component.find(AxisSettingsPopover).at(0).prop('dataBounds')).toEqual({ - min: -5, - max: 50, - }); + userEvent.click(getLeftAxisButton()); + fireEvent.click(screen.getByTestId('lnsXY_axisExtent_groups_custom')); + expect(screen.getByTestId('lnsXY_axisExtent_lowerBound')).toHaveValue(-5); + expect(screen.getByTestId('lnsXY_axisExtent_upperBound')).toHaveValue(50); }); it('should pass in extent information', () => { const state = testState(); - const component = shallow( + render( { }} /> ); - - expect(component.find(AxisSettingsPopover).at(0).prop('extent')).toEqual({ - mode: 'custom', - lowerBound: 123, - upperBound: 456, - }); - expect(component.find(AxisSettingsPopover).at(0).prop('setExtent')).toBeTruthy(); - expect(component.find(AxisSettingsPopover).at(1).prop('extent')).toBeFalsy(); - expect(component.find(AxisSettingsPopover).at(1).prop('setExtent')).toBeTruthy(); - // default extent - expect(component.find(AxisSettingsPopover).at(2).prop('extent')).toEqual({ - mode: 'full', - }); - expect(component.find(AxisSettingsPopover).at(2).prop('setExtent')).toBeTruthy(); - }); - }); - - describe('Dimension Editor', () => { - test('shows the correct axis side options when in horizontal mode', () => { - const state = testState(); - const component = mount( - - ); - - const options = component - .find(EuiButtonGroup) - .first() - .prop('options') as EuiButtonGroupProps['options']; - - expect(options!.map(({ label }) => label)).toEqual(['Bottom', 'Auto', 'Top']); - }); - - test('shows the default axis side options when not in horizontal mode', () => { - const state = testState(); - const component = mount( - - ); - - const options = component - .find(EuiButtonGroup) - .first() - .prop('options') as EuiButtonGroupProps['options']; - - expect(options!.map(({ label }) => label)).toEqual(['Left', 'Auto', 'Right']); - }); - - test('sets the color of a dimension to the color from palette service if not set explicitly', () => { - const state = { - ...testState(), - layers: [ - { - seriesType: 'bar', - layerType: LayerTypes.DATA, - layerId: 'first', - splitAccessor: undefined, - xAccessor: 'foo', - accessors: ['bar'], - }, - ], - } as XYState; - const component = mount( - - ); - - expect(component.find(EuiColorPicker).prop('color')).toEqual('black'); - }); - - test('uses the overwrite color if set', () => { - const state = { - ...testState(), - layers: [ - { - seriesType: 'bar', - layerType: LayerTypes.DATA, - layerId: 'first', - splitAccessor: undefined, - xAccessor: 'foo', - accessors: ['bar'], - yConfig: [{ forAccessor: 'bar', color: 'red' }], - }, - ], - } as XYState; - - const component = mount( - - ); - - expect(component.find(EuiColorPicker).prop('color')).toEqual('red'); - }); - test('does not apply incorrect color', () => { - const setState = jest.fn(); - const state = { - ...testState(), - layers: [ - { - seriesType: 'bar', - layerType: LayerTypes.DATA, - layerId: 'first', - splitAccessor: undefined, - xAccessor: 'foo', - accessors: ['bar'], - yConfig: [{ forAccessor: 'bar', color: 'red' }], - }, - ], - } as XYState; - - const component = mount( - - ); - - act(() => { - component - .find('input[data-test-subj="euiColorPickerAnchor indexPattern-dimension-colorPicker"]') - .simulate('change', { - target: { value: 'INCORRECT_COLOR' }, - }); - }); - component.update(); - expect(component.find(EuiColorPicker).prop('color')).toEqual('INCORRECT_COLOR'); - expect(setState).not.toHaveBeenCalled(); - - act(() => { - component - .find('input[data-test-subj="euiColorPickerAnchor indexPattern-dimension-colorPicker"]') - .simulate('change', { - target: { value: '666666' }, - }); - }); - component.update(); - expect(component.find(EuiColorPicker).prop('color')).toEqual('666666'); - expect(setState).toHaveBeenCalled(); + userEvent.click(getLeftAxisButton()); + expect(screen.getByTestId('lnsXY_axisExtent_lowerBound')).toHaveValue(123); + expect(screen.getByTestId('lnsXY_axisExtent_upperBound')).toHaveValue(456); + userEvent.click(getRightAxisButton()); + const selectedButton = getSelectedButtonInGroup( + 'lnsXY_axisBounds_groups', + within(screen.getByRole('dialog', { name: 'Right axis' })) + )(); + + expect(selectedButton).toHaveTextContent('Full'); }); }); }); diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts b/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts index b9d7a7042e530..77f79b5db6b1a 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts @@ -10,38 +10,17 @@ import type { TableSuggestionColumn, VisualizationSuggestion, TableSuggestion } import { State, XYState, - visualizationTypes, + visualizationSubtypes, XYAnnotationLayerConfig, XYDataLayerConfig, } from './types'; import { generateId } from '../../id_generator'; -import { getXyVisualization } from './xy_visualization'; -import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; -import { eventAnnotationServiceMock } from '@kbn/event-annotation-plugin/public/mocks'; import { type PaletteOutput, DEFAULT_COLOR_MAPPING_CONFIG } from '@kbn/coloring'; import { LayerTypes } from '@kbn/expression-xy-plugin/public'; -import { fieldFormatsServiceMock } from '@kbn/field-formats-plugin/public/mocks'; -import { coreMock, themeServiceMock } from '@kbn/core/public/mocks'; -import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; -import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; -import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; -import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; +import { getVisualizationSubtypeId } from './visualization_helpers'; jest.mock('../../id_generator'); -const xyVisualization = getXyVisualization({ - paletteService: chartPluginMock.createPaletteRegistry(), - fieldFormats: fieldFormatsServiceMock.createStartContract(), - useLegacyTimeAxis: false, - kibanaTheme: themeServiceMock.createStartContract(), - eventAnnotationService: eventAnnotationServiceMock, - core: coreMock.createStart(), - storage: {} as IStorageWrapper, - data: dataPluginMock.createStartContract(), - unifiedSearch: unifiedSearchPluginMock.createStartContract(), - dataViewsService: {} as DataViewsPublicPluginStart, -}); - describe('xy_suggestions', () => { function numCol(columnId: string): TableSuggestionColumn { return { @@ -247,8 +226,8 @@ describe('xy_suggestions', () => { }, }); - expect(suggestions).toHaveLength(visualizationTypes.length); - expect(suggestions.map(({ state }) => xyVisualization.getVisualizationTypeId(state))).toEqual([ + expect(suggestions).toHaveLength(visualizationSubtypes.length); + expect(suggestions.map(({ state }) => getVisualizationSubtypeId(state))).toEqual([ 'line', 'bar', 'bar_horizontal', @@ -274,8 +253,8 @@ describe('xy_suggestions', () => { keptLayerIds: [], }); - expect(suggestions).toHaveLength(visualizationTypes.length); - expect(suggestions.map(({ state }) => xyVisualization.getVisualizationTypeId(state))).toEqual([ + expect(suggestions).toHaveLength(visualizationSubtypes.length); + expect(suggestions.map(({ state }) => getVisualizationSubtypeId(state))).toEqual([ 'bar_stacked', 'bar', 'bar_horizontal', @@ -302,11 +281,11 @@ describe('xy_suggestions', () => { keptLayerIds: ['first', 'second'], }); - expect(suggestions).toHaveLength(visualizationTypes.length); + expect(suggestions).toHaveLength(visualizationSubtypes.length); expect(suggestions.map(({ state }) => state.layers.length)).toEqual([ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ]); - expect(suggestions.map(({ state }) => xyVisualization.getVisualizationTypeId(state))).toEqual([ + expect(suggestions.map(({ state }) => getVisualizationSubtypeId(state))).toEqual([ 'bar_stacked', 'bar', 'bar_horizontal', @@ -347,8 +326,8 @@ describe('xy_suggestions', () => { }, }); - expect(suggestions).toHaveLength(visualizationTypes.length); - expect(suggestions.map(({ state }) => xyVisualization.getVisualizationTypeId(state))).toEqual([ + expect(suggestions).toHaveLength(visualizationSubtypes.length); + expect(suggestions.map(({ state }) => getVisualizationSubtypeId(state))).toEqual([ 'line', 'bar', 'bar_horizontal', @@ -397,8 +376,8 @@ describe('xy_suggestions', () => { }, }); - expect(suggestions).toHaveLength(visualizationTypes.length); - expect(suggestions.map(({ state }) => xyVisualization.getVisualizationTypeId(state))).toEqual([ + expect(suggestions).toHaveLength(visualizationSubtypes.length); + expect(suggestions.map(({ state }) => getVisualizationSubtypeId(state))).toEqual([ 'line', 'bar', 'bar_horizontal', @@ -460,8 +439,8 @@ describe('xy_suggestions', () => { }, }); - expect(suggestions).toHaveLength(visualizationTypes.length); - expect(suggestions.map(({ state }) => xyVisualization.getVisualizationTypeId(state))).toEqual([ + expect(suggestions).toHaveLength(visualizationSubtypes.length); + expect(suggestions.map(({ state }) => getVisualizationSubtypeId(state))).toEqual([ 'line', // line + line = line 'mixed', // any other combination is mixed 'mixed', @@ -499,7 +478,7 @@ describe('xy_suggestions', () => { keptLayerIds: [], }); - expect(rest).toHaveLength(visualizationTypes.length - 1); + expect(rest).toHaveLength(visualizationSubtypes.length - 1); expect(suggestionSubset(suggestion)).toMatchInlineSnapshot(` Array [ Object { @@ -526,7 +505,7 @@ describe('xy_suggestions', () => { keptLayerIds: [], }); - expect(rest).toHaveLength(visualizationTypes.length - 1); + expect(rest).toHaveLength(visualizationSubtypes.length - 1); expect(suggestionSubset(suggestion)).toMatchInlineSnapshot(` Array [ Object { @@ -572,7 +551,7 @@ describe('xy_suggestions', () => { keptLayerIds: [], }); - expect(rest).toHaveLength(visualizationTypes.length - 1); + expect(rest).toHaveLength(visualizationSubtypes.length - 1); expect(suggestionSubset(suggestion)).toMatchInlineSnapshot(` Array [ Object { @@ -616,7 +595,7 @@ describe('xy_suggestions', () => { keptLayerIds: [], }); - expect(rest).toHaveLength(visualizationTypes.length - 1); + expect(rest).toHaveLength(visualizationSubtypes.length - 1); expect(suggestion.title).toEqual('Bar vertical stacked'); expect(suggestion.state).toEqual( expect.objectContaining({ @@ -907,7 +886,7 @@ describe('xy_suggestions', () => { keptLayerIds: ['first'], }); - expect(suggestions).toHaveLength(visualizationTypes.length); + expect(suggestions).toHaveLength(visualizationSubtypes.length); expect(suggestions[0].hide).toEqual(false); expect(suggestions[0].state).toEqual({ @@ -956,7 +935,7 @@ describe('xy_suggestions', () => { keptLayerIds: ['first'], }); - expect(rest).toHaveLength(visualizationTypes.length - 2); + expect(rest).toHaveLength(visualizationSubtypes.length - 2); expect(seriesSuggestion.state).toEqual({ ...currentState, preferredSeriesType: 'line', @@ -1012,7 +991,7 @@ describe('xy_suggestions', () => { keptLayerIds: [], }); - expect(rest).toHaveLength(visualizationTypes.length - 1); + expect(rest).toHaveLength(visualizationSubtypes.length - 1); expect(suggestion.state.preferredSeriesType).toEqual('bar_horizontal'); expect( (suggestion.state.layers as XYDataLayerConfig[]).every( diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.ts b/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.ts index 847876f5c0ebd..0974e50ef36fe 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.ts @@ -20,12 +20,12 @@ import { getColorMappingDefaults } from '../../utils'; import { State, XYState, - visualizationTypes, + visualizationSubtypes, XYLayerConfig, XYDataLayerConfig, SeriesType, } from './types'; -import { getIconForSeries } from './state_helpers'; +import { flipSeriesType, getIconForSeries } from './state_helpers'; import { getDataLayers, isDataLayer } from './visualization_helpers'; const columnSortOrder = { @@ -137,23 +137,6 @@ function getSuggestionForColumns( } } -function flipSeriesType(seriesType: SeriesType) { - switch (seriesType) { - case 'bar_horizontal': - return 'bar'; - case 'bar_horizontal_stacked': - return 'bar_stacked'; - case 'bar': - return 'bar_horizontal'; - case 'bar_horizontal_percentage_stacked': - return 'bar_percentage_stacked'; - case 'bar_percentage_stacked': - return 'bar_horizontal_percentage_stacked'; - default: - return 'bar_horizontal'; - } -} - function getBucketMappings(table: TableSuggestion, currentState?: State) { const currentLayer = currentState && @@ -255,7 +238,7 @@ function getSuggestionsForLayer({ // handles the simplest cases, acting as a chart switcher if (!currentState && changeType === 'unchanged') { // Chart switcher needs to include every chart type - return visualizationTypes + return visualizationSubtypes .map((visType) => { return { ...buildSuggestion({ @@ -337,7 +320,7 @@ function getSuggestionsForLayer({ // Combine all pre-built suggestions with hidden suggestions for remaining chart types return sameStateSuggestions.concat( - visualizationTypes + visualizationSubtypes .filter((visType) => { return !sameStateSuggestions.find( (suggestion) => suggestion.state.preferredSeriesType === visType.id @@ -661,7 +644,7 @@ function getScore( ? 0.5 : 1; // chart with multiple y values and split series will have a score of 1, single y value and no split series reduce score - return (((yValues.length > 1 ? 2 : 1) + (splitBy ? 1 : 0)) / 3) * changeFactor; + return (((yValues.length > 1 ? 3 : 2) + (splitBy ? 1 : 0)) / 4) * changeFactor; } function getExistingLayer(currentState: XYState | undefined, layerId: string) { diff --git a/x-pack/plugins/maps/public/lens/choropleth_chart/visualization.tsx b/x-pack/plugins/maps/public/lens/choropleth_chart/visualization.tsx index fd7ff91b78f8b..8c844cc4fb313 100644 --- a/x-pack/plugins/maps/public/lens/choropleth_chart/visualization.tsx +++ b/x-pack/plugins/maps/public/lens/choropleth_chart/visualization.tsx @@ -32,23 +32,21 @@ export const getVisualization = ({ }): Visualization => ({ id: 'lnsChoropleth', + getVisualizationTypeId() { + return this.id; + }, visualizationTypes: [ { id: 'lnsChoropleth', icon: IconRegionMap, label: CHART_LABEL, - groupLabel: i18n.translate('xpack.maps.lens.groupLabel', { - defaultMessage: 'Map', + sortPriority: 10, + description: i18n.translate('xpack.maps.regionMap.visualizationDescription', { + defaultMessage: 'Show geographic data using colored regions.', }), - sortPriority: 1, - showExperimentalBadge: false, }, ], - getVisualizationTypeId() { - return 'lnsChoropleth'; - }, - clearLayer(state) { const newState = { ...state }; delete newState.emsLayerId; diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 760d745ebe0c8..301dac2305e30 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -25122,7 +25122,6 @@ "xpack.lens.configPanel.selectAnnotationMethod": "Sélectionnez une méthode d’annotation", "xpack.lens.configPanel.selectLayerType": "Sélectionner le type de calque", "xpack.lens.configPanel.selectVisualization": "Sélectionner une visualisation", - "xpack.lens.configPanel.visualizationType": "Type de visualisation", "xpack.lens.configure.emptyConfig": "Ajouter ou glisser-déposer un champ", "xpack.lens.configure.invalidBottomReferenceLineDimension": "La ligne de référence est affectée à un axe qui n’existe plus ou qui n’est plus valide. Vous pouvez déplacer cette ligne de référence vers un autre axe disponible ou la supprimer.", "xpack.lens.configure.invalidConfigTooltip": "Configuration non valide.", @@ -25144,7 +25143,6 @@ "xpack.lens.datatable.column.help": "Colonne de tableau de données", "xpack.lens.datatable.conjunctionSign": " & ", "xpack.lens.datatable.expressionHelpLabel": "Outil de rendu de tableaux de données", - "xpack.lens.datatable.groupLabel": "Tabulaire", "xpack.lens.datatable.headingLabel": "Valeur", "xpack.lens.datatable.label": "Tableau", "xpack.lens.datatable.metric": "Indicateur", @@ -25283,7 +25281,6 @@ "xpack.lens.guageVisualization.chartCannotRenderMinGreaterMax": "La valeur minimale peut ne pas être supérieure à la valeur maximale.", "xpack.lens.heatmap.addLayer": "Visualisation", "xpack.lens.heatmap.cellValueLabel": "Valeur de cellule", - "xpack.lens.heatmap.groupLabel": "Carte thermique", "xpack.lens.heatmap.headingLabel": "Valeur", "xpack.lens.heatmap.heatmapLabel": "Carte thermique", "xpack.lens.heatmap.horizontalAxisDisabledHelpText": "Ce paramètre s'applique uniquement lorsque l'axe horizontal est activé.", @@ -25293,7 +25290,6 @@ "xpack.lens.heatmapChart.legendVisibility.hide": "Masquer", "xpack.lens.heatmapChart.legendVisibility.show": "Afficher", "xpack.lens.heatmapVisualization.arrayValuesWarningMessage": "{label} contient des valeurs de tableau. Le rendu de votre visualisation peut ne pas se présenter comme attendu.", - "xpack.lens.heatmapVisualization.heatmapGroupLabel": "Magnitude", "xpack.lens.heatmapVisualization.heatmapLabel": "Carte thermique", "xpack.lens.heatmapVisualization.missingXAccessorLongMessage": "La configuration de l'axe horizontal est manquante.", "xpack.lens.heatmapVisualization.missingXAccessorShortMessage": "Axe horizontal manquant.", @@ -25683,7 +25679,6 @@ "xpack.lens.layerActions.layerSettingsAction": "Paramètres du calque", "xpack.lens.layerPanel.ignoreGlobalFilters": "Ignorer les filtres globaux", "xpack.lens.layerPanel.missingDataView": "Vue de données introuvable", - "xpack.lens.layerPanel.selectVisualizationType": "Sélectionner le type de visualisation", "xpack.lens.layerSettings.ignoreGlobalFilters": "Utiliser les filtres globaux", "xpack.lens.layerTitle.fallbackLabel": "Calque", "xpack.lens.legacyMetric.addLayer": "Visualisation", @@ -25694,7 +25689,6 @@ "xpack.lens.legacyMetric.dynamicColoring.label": "Couleur par valeur", "xpack.lens.legacyMetric.dynamicColoring.none": "Aucun", "xpack.lens.legacyMetric.dynamicColoring.text": "Texte", - "xpack.lens.legacyMetric.groupLabel": "Valeur d’objectif et unique", "xpack.lens.legacyMetric.label": "Ancien indicateur", "xpack.lens.legacyMetric.metricSize.extraLarge": "XL", "xpack.lens.legacyMetric.metricSize.extraSmall": "XS", @@ -25727,7 +25721,6 @@ "xpack.lens.metric.color": "Couleur", "xpack.lens.metric.colorMode.dynamic": "Dynamique", "xpack.lens.metric.colorMode.static": "Statique", - "xpack.lens.metric.groupLabel": "Valeur d’objectif et unique", "xpack.lens.metric.headingLabel": "Valeur", "xpack.lens.metric.icon": "Décoration de l’icône", "xpack.lens.metric.iconSelect.computeLabel": "Calcul", @@ -25776,8 +25769,6 @@ "xpack.lens.pie.addLayer": "Visualisation", "xpack.lens.pie.arrayValues": "Les dimensions suivantes contiennent des valeurs de tableau : {label}. Le rendu de votre visualisation peut ne pas se présenter comme attendu.", "xpack.lens.pie.collapsedDimensionsDontCount": "(Les dimensions réduites ne sont pas concernées par cette limite.)", - "xpack.lens.pie.donutLabel": "Graphique en anneau", - "xpack.lens.pie.groupLabel": "Proportion", "xpack.lens.pie.groupMetricLabel": "Indicateurs", "xpack.lens.pie.groupMetricLabelSingular": "Indicateur", "xpack.lens.pie.headingLabel": "Valeur", @@ -25801,29 +25792,17 @@ "xpack.lens.pie.verticalAxisLabel": "Axe vertical", "xpack.lens.pie.wafflelabel": "Gaufre", "xpack.lens.pie.waffleSuggestionLabel": "Gaufre", - "xpack.lens.pieChart.categoriesInLegendLabel": "Masquer les étiquettes", "xpack.lens.pieChart.colorPicker.disabledBecauseGroupBy": "Vous ne pouvez pas appliquer de couleurs personnalisées à des sections individuelles lorsque le calque inclut une ou plusieurs dimensions \"Regrouper par\".", "xpack.lens.pieChart.colorPicker.disabledBecauseSliceBy": "Vous ne pouvez pas appliquer de couleurs personnalisées à des sections individuelles lorsque le calque inclut une ou plusieurs dimensions \"Section par\".", - "xpack.lens.pieChart.emptySizeRatioLabel": "Taille de la zone intérieure", "xpack.lens.pieChart.emptySizeRatioOptions.large": "Large", "xpack.lens.pieChart.emptySizeRatioOptions.medium": "Moyenne", "xpack.lens.pieChart.emptySizeRatioOptions.small": "Petite", - "xpack.lens.pieChart.fitInsideOnlyLabel": "À l'intérieur uniquement", - "xpack.lens.pieChart.hiddenNumbersLabel": "Masquer dans le graphique", - "xpack.lens.pieChart.labelPositionLabel": "Position", "xpack.lens.pieChart.legendVisibility.auto": "Auto", "xpack.lens.pieChart.legendVisibility.hide": "Masquer", "xpack.lens.pieChart.legendVisibility.show": "Afficher", "xpack.lens.pieChart.multipleMetrics": "Plusieurs indicateurs", "xpack.lens.pieChart.nestedLegendLabel": "Imbriqué", - "xpack.lens.pieChart.numberLabels": "Valeurs", - "xpack.lens.pieChart.percentDecimalsLabel": "Nombre maximal de décimales pour les pourcentages", - "xpack.lens.pieChart.showCategoriesLabel": "Intérieur ou extérieur", - "xpack.lens.pieChart.showFormatterValuesLabel": "Afficher la valeur", - "xpack.lens.pieChart.showPercentValuesLabel": "Afficher le pourcentage", "xpack.lens.pieChart.showTreemapCategoriesLabel": "Afficher les étiquettes", - "xpack.lens.pieChart.valuesLabel": "Étiquettes", - "xpack.lens.pieChart.visualOptionsLabel": "Options visuelles", "xpack.lens.primaryMetric.headingLabel": "Valeur", "xpack.lens.primaryMetric.label": "Indicateur principal", "xpack.lens.reducedTimeRangeSuffix": "dernière {reducedTimeRange}", @@ -25841,10 +25820,7 @@ "xpack.lens.share.csvButton": "Télécharger CSV", "xpack.lens.share.export": "Exporter un fichier", "xpack.lens.share.helpText": "Exportez un CVS de cette visualisation.", - "xpack.lens.shared.AppearanceLabel": "Apparence", "xpack.lens.shared.axisNameLabel": "Titre de l'axe", - "xpack.lens.shared.chartValueLabelVisibilityLabel": "Étiquettes", - "xpack.lens.shared.chartValueLabelVisibilityTooltip": "Si l'espace est insuffisant, les étiquettes de valeurs pourront être masquées", "xpack.lens.shared.labelTruncation": "Troncature d'étiquette", "xpack.lens.shared.Lagend ": "Ligne d'en-tête", "xpack.lens.shared.legendAlignmentLabel": "Alignement", @@ -25905,7 +25881,6 @@ "xpack.lens.shared.legendValues.varianceDesc": "La variance de toutes les valeurs de la série.", "xpack.lens.shared.legendVisibilityLabel": "Visibilité", "xpack.lens.shared.maxLinesLabel": "Limite de ligne", - "xpack.lens.shared.metric.appearanceLabel": "Apparence", "xpack.lens.shared.nestedLegendLabel": "Imbriqué", "xpack.lens.shared.overwriteAxisTitle": "Écraser le titre de l'axe", "xpack.lens.shared.overwriteLegendTitle": "Remplacer l'en-tête de la série", @@ -25914,7 +25889,6 @@ "xpack.lens.shared.ticksPositionOptionsTooltip": "Place les coches sur chaque bordure de bande au lieu de les répartir de manière homogène.", "xpack.lens.shared.valueInLegendLabel": "Afficher la valeur", "xpack.lens.shared.valueLabelsVisibility.auto": "Masquer", - "xpack.lens.shared.valueLabelsVisibility.inside": "Afficher", "xpack.lens.staticValue.headingLabel": "Placement", "xpack.lens.sugegstion.refreshSuggestionLabel": "Actualiser", "xpack.lens.suggestion.refreshSuggestionTooltip": "Actualisez les suggestions en fonction de la visualisation sélectionnée.", @@ -26032,7 +26006,6 @@ "xpack.lens.xyChart.fill.below": "En dessous", "xpack.lens.xyChart.fill.label": "Remplir", "xpack.lens.xyChart.fill.none": "Aucun", - "xpack.lens.xyChart.fillOpacityLabel": "Opacité de remplissage", "xpack.lens.xyChart.Gridlines": "Quadrillage", "xpack.lens.xyChart.horizontalAxisLabel": "Axe horizontal", "xpack.lens.xyChart.horizontalLeftAxisLabel": "Axe supérieur horizontal", @@ -26097,7 +26070,6 @@ "xpack.lens.xySuggestions.yAxixConjunctionSign": " & ", "xpack.lens.xyVisualization.areaLabel": "Aire", "xpack.lens.xyVisualization.arrayValues": "{label} contient des valeurs de tableau. Le rendu de votre visualisation peut ne pas se présenter comme attendu.", - "xpack.lens.xyVisualization.barGroupLabel": "Barres", "xpack.lens.xyVisualization.barHorizontalFullLabel": "Horizontal à barres", "xpack.lens.xyVisualization.barHorizontalLabel": "H. Barres", "xpack.lens.xyVisualization.barLabel": "Vertical à barres", @@ -26109,7 +26081,6 @@ "xpack.lens.xyVisualization.dataTypeFailureXShort": "Type de données incorrect pour {axis}.", "xpack.lens.xyVisualization.dataTypeFailureYLong": "La dimension {label} fournie pour {axis} possède un type de données incorrect. Nombre attendu mais possède {dataType}", "xpack.lens.xyVisualization.dataTypeFailureYShort": "Type de données incorrect pour {axis}.", - "xpack.lens.xyVisualization.lineGroupLabel": "Linéaire et en aires", "xpack.lens.xyVisualization.lineLabel": "Ligne", "xpack.lens.xyVisualization.mixedBarHorizontalLabel": "Horizontal à barres mixte", "xpack.lens.xyVisualization.mixedLabel": "XY mixte", @@ -26704,10 +26675,7 @@ "xpack.maps.lens.choropleth.label": "Carte de région", "xpack.maps.lens.choroplethChart.addLayer": "Ajouter un calque de visualisation", "xpack.maps.lens.choroplethChart.choroplethLayerLabel": "{emsLayerLabel} par {accessorLabel}", - "xpack.maps.lens.choroplethChart.metricValueLabel": "Indicateur", - "xpack.maps.lens.choroplethChart.regionKeyLabel": "Clé de région", "xpack.maps.lens.choroplethChart.suggestionLabel": "{emsLayerLabel} par {metricLabel}", - "xpack.maps.lens.groupLabel": "Carte", "xpack.maps.loadMap.errorAttemptingToLoadSavedMap": "Impossible de charger la carte", "xpack.maps.logDatatable.region": "Clé de région", "xpack.maps.logDatatable.value": "Valeur", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 366d85478acda..6cc7343799696 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -25111,7 +25111,6 @@ "xpack.lens.configPanel.selectAnnotationMethod": "注釈方法を選択", "xpack.lens.configPanel.selectLayerType": "レイヤータイプを選択", "xpack.lens.configPanel.selectVisualization": "ビジュアライゼーションを選択してください", - "xpack.lens.configPanel.visualizationType": "ビジュアライゼーションタイプ", "xpack.lens.configure.emptyConfig": "フィールドを追加するか、ドラッグアンドドロップします", "xpack.lens.configure.invalidBottomReferenceLineDimension": "この基準線は存在しないか有効ではない軸に割り当てられています。この基準線を別の使用可能な軸に移動するか、削除することができます。", "xpack.lens.configure.invalidConfigTooltip": "無効な構成です。", @@ -25133,7 +25132,6 @@ "xpack.lens.datatable.column.help": "データテーブル列", "xpack.lens.datatable.conjunctionSign": " & ", "xpack.lens.datatable.expressionHelpLabel": "データベースレンダー", - "xpack.lens.datatable.groupLabel": "タブ", "xpack.lens.datatable.headingLabel": "値", "xpack.lens.datatable.label": "表", "xpack.lens.datatable.metric": "メトリック", @@ -25272,7 +25270,6 @@ "xpack.lens.guageVisualization.chartCannotRenderMinGreaterMax": "最小値は最大値以下でなければなりません", "xpack.lens.heatmap.addLayer": "ビジュアライゼーション", "xpack.lens.heatmap.cellValueLabel": "セル値", - "xpack.lens.heatmap.groupLabel": "ヒートマップ", "xpack.lens.heatmap.headingLabel": "値", "xpack.lens.heatmap.heatmapLabel": "ヒートマップ", "xpack.lens.heatmap.horizontalAxisDisabledHelpText": "この設定は、横軸が有効であるときにのみ適用されます。", @@ -25282,7 +25279,6 @@ "xpack.lens.heatmapChart.legendVisibility.hide": "非表示", "xpack.lens.heatmapChart.legendVisibility.show": "表示", "xpack.lens.heatmapVisualization.arrayValuesWarningMessage": "{label}には配列値が含まれます。可視化が想定通りに表示されない場合があります。", - "xpack.lens.heatmapVisualization.heatmapGroupLabel": "大きさ", "xpack.lens.heatmapVisualization.heatmapLabel": "ヒートマップ", "xpack.lens.heatmapVisualization.missingXAccessorLongMessage": "横軸の構成がありません。", "xpack.lens.heatmapVisualization.missingXAccessorShortMessage": "横軸がありません。", @@ -25671,7 +25667,6 @@ "xpack.lens.layerActions.layerSettingsAction": "レイヤー設定", "xpack.lens.layerPanel.ignoreGlobalFilters": "グローバルフィルターを無視", "xpack.lens.layerPanel.missingDataView": "データビューが見つかりません", - "xpack.lens.layerPanel.selectVisualizationType": "ビジュアライゼーションタイプを選択", "xpack.lens.layerSettings.ignoreGlobalFilters": "グローバルフィルターを使用", "xpack.lens.layerTitle.fallbackLabel": "レイヤー", "xpack.lens.legacyMetric.addLayer": "ビジュアライゼーション", @@ -25681,8 +25676,7 @@ "xpack.lens.legacyMetric.dynamicColoring.background": "塗りつぶし", "xpack.lens.legacyMetric.dynamicColoring.label": "値別の色", "xpack.lens.legacyMetric.dynamicColoring.none": "なし", - "xpack.lens.legacyMetric.dynamicColoring.text": "Text", - "xpack.lens.legacyMetric.groupLabel": "目標値と単一の値", + "xpack.lens.legacyMetric.dynamicColoring.text": "テキスト", "xpack.lens.legacyMetric.label": "レガシーメトリック", "xpack.lens.legacyMetric.metricSize.extraLarge": "XL", "xpack.lens.legacyMetric.metricSize.extraSmall": "XS", @@ -25715,7 +25709,6 @@ "xpack.lens.metric.color": "色", "xpack.lens.metric.colorMode.dynamic": "動的", "xpack.lens.metric.colorMode.static": "静的", - "xpack.lens.metric.groupLabel": "目標値と単一の値", "xpack.lens.metric.headingLabel": "値", "xpack.lens.metric.icon": "アイコン装飾", "xpack.lens.metric.iconSelect.computeLabel": "演算", @@ -25764,8 +25757,6 @@ "xpack.lens.pie.addLayer": "ビジュアライゼーション", "xpack.lens.pie.arrayValues": "次のディメンションには配列値があります:{label}。可視化が想定通りに表示されない場合があります。", "xpack.lens.pie.collapsedDimensionsDontCount": "(折りたたまれたディメンションはこの制限に対してカウントされません。)", - "xpack.lens.pie.donutLabel": "ドーナッツ", - "xpack.lens.pie.groupLabel": "比率", "xpack.lens.pie.groupMetricLabel": "メトリック", "xpack.lens.pie.groupMetricLabelSingular": "メトリック", "xpack.lens.pie.headingLabel": "値", @@ -25789,29 +25780,17 @@ "xpack.lens.pie.verticalAxisLabel": "縦軸", "xpack.lens.pie.wafflelabel": "ワッフル", "xpack.lens.pie.waffleSuggestionLabel": "ワッフル", - "xpack.lens.pieChart.categoriesInLegendLabel": "ラベルを非表示", "xpack.lens.pieChart.colorPicker.disabledBecauseGroupBy": "レイヤーに1つ以上の「グループ化基準」ディメンションが含まれる場合、個々のスライスにカスタムカラーを適用することができません。", "xpack.lens.pieChart.colorPicker.disabledBecauseSliceBy": "レイヤーに1つ以上の「スライス基準」ディメンションが含まれる場合、個々のスライスにカスタムカラーを適用することができません。", - "xpack.lens.pieChart.emptySizeRatioLabel": "内側の領域のサイズ", "xpack.lens.pieChart.emptySizeRatioOptions.large": "大", "xpack.lens.pieChart.emptySizeRatioOptions.medium": "中", "xpack.lens.pieChart.emptySizeRatioOptions.small": "小", - "xpack.lens.pieChart.fitInsideOnlyLabel": "内部のみ", - "xpack.lens.pieChart.hiddenNumbersLabel": "グラフから非表示", - "xpack.lens.pieChart.labelPositionLabel": "位置", "xpack.lens.pieChart.legendVisibility.auto": "自動", "xpack.lens.pieChart.legendVisibility.hide": "非表示", "xpack.lens.pieChart.legendVisibility.show": "表示", "xpack.lens.pieChart.multipleMetrics": "複数のメトリック", "xpack.lens.pieChart.nestedLegendLabel": "Nested", - "xpack.lens.pieChart.numberLabels": "値", - "xpack.lens.pieChart.percentDecimalsLabel": "割合の最大小数点桁数", - "xpack.lens.pieChart.showCategoriesLabel": "内部または外部", - "xpack.lens.pieChart.showFormatterValuesLabel": "値を表示", - "xpack.lens.pieChart.showPercentValuesLabel": "割合を表示", "xpack.lens.pieChart.showTreemapCategoriesLabel": "ラベルを表示", - "xpack.lens.pieChart.valuesLabel": "ラベル", - "xpack.lens.pieChart.visualOptionsLabel": "視覚オプション", "xpack.lens.primaryMetric.headingLabel": "値", "xpack.lens.primaryMetric.label": "主メトリック", "xpack.lens.reducedTimeRangeSuffix": "last {reducedTimeRange}", @@ -25829,10 +25808,7 @@ "xpack.lens.share.csvButton": "CSV をダウンロード", "xpack.lens.share.export": "ファイルのエクスポート", "xpack.lens.share.helpText": "このビジュアライゼーションのCSVをエクスポートします。", - "xpack.lens.shared.AppearanceLabel": "見た目", "xpack.lens.shared.axisNameLabel": "軸のタイトル", - "xpack.lens.shared.chartValueLabelVisibilityLabel": "ラベル", - "xpack.lens.shared.chartValueLabelVisibilityTooltip": "十分なスペースがない場合、値ラベルが非表示になることがあります。", "xpack.lens.shared.labelTruncation": "ラベルの切り捨て", "xpack.lens.shared.Lagend ": "系列ヘッダー", "xpack.lens.shared.legendAlignmentLabel": "アラインメント", @@ -25893,7 +25869,6 @@ "xpack.lens.shared.legendValues.varianceDesc": "系列のすべての値のばらつき。", "xpack.lens.shared.legendVisibilityLabel": "レイヤー表示のズーム範囲", "xpack.lens.shared.maxLinesLabel": "行の制限", - "xpack.lens.shared.metric.appearanceLabel": "見た目", "xpack.lens.shared.nestedLegendLabel": "Nested", "xpack.lens.shared.overwriteAxisTitle": "軸タイトルを上書き", "xpack.lens.shared.overwriteLegendTitle": "系列ヘッダーを上書き", @@ -25902,7 +25877,6 @@ "xpack.lens.shared.ticksPositionOptionsTooltip": "目盛を均等に分布するのではなく、各帯の境界に目盛を表示します", "xpack.lens.shared.valueInLegendLabel": "値を表示", "xpack.lens.shared.valueLabelsVisibility.auto": "非表示", - "xpack.lens.shared.valueLabelsVisibility.inside": "表示", "xpack.lens.staticValue.headingLabel": "配置", "xpack.lens.sugegstion.refreshSuggestionLabel": "更新", "xpack.lens.suggestion.refreshSuggestionTooltip": "選択したビジュアライゼーションに基づいて、候補を更新します。", @@ -26021,7 +25995,6 @@ "xpack.lens.xyChart.fill.below": "下", "xpack.lens.xyChart.fill.label": "塗りつぶし", "xpack.lens.xyChart.fill.none": "なし", - "xpack.lens.xyChart.fillOpacityLabel": "塗りつぶしの透明度", "xpack.lens.xyChart.Gridlines": "グリッド線", "xpack.lens.xyChart.horizontalAxisLabel": "横軸", "xpack.lens.xyChart.horizontalLeftAxisLabel": "横上軸", @@ -26086,7 +26059,6 @@ "xpack.lens.xySuggestions.yAxixConjunctionSign": " & ", "xpack.lens.xyVisualization.areaLabel": "エリア", "xpack.lens.xyVisualization.arrayValues": "{label}には配列値が含まれます。可視化が想定通りに表示されない場合があります。", - "xpack.lens.xyVisualization.barGroupLabel": "棒", "xpack.lens.xyVisualization.barHorizontalFullLabel": "横棒", "xpack.lens.xyVisualization.barHorizontalLabel": "H.棒", "xpack.lens.xyVisualization.barLabel": "縦棒", @@ -26098,7 +26070,6 @@ "xpack.lens.xyVisualization.dataTypeFailureXShort": "{axis}のデータ型が正しくありません。", "xpack.lens.xyVisualization.dataTypeFailureYLong": "{axis}のディメンション{label}のデータ型が正しくありません。数値が想定されていますが、{dataType}です", "xpack.lens.xyVisualization.dataTypeFailureYShort": "{axis}のデータ型が正しくありません。", - "xpack.lens.xyVisualization.lineGroupLabel": "折れ線と面", "xpack.lens.xyVisualization.lineLabel": "折れ線", "xpack.lens.xyVisualization.mixedBarHorizontalLabel": "混在した横棒", "xpack.lens.xyVisualization.mixedLabel": "ミックスされた XY", @@ -26693,10 +26664,7 @@ "xpack.maps.lens.choropleth.label": "地域マップ", "xpack.maps.lens.choroplethChart.addLayer": "ビジュアライゼーションレイヤーを追加", "xpack.maps.lens.choroplethChart.choroplethLayerLabel": "{accessorLabel}別{emsLayerLabel}", - "xpack.maps.lens.choroplethChart.metricValueLabel": "メトリック", - "xpack.maps.lens.choroplethChart.regionKeyLabel": "リージョンキー", "xpack.maps.lens.choroplethChart.suggestionLabel": "{metricLabel}別{emsLayerLabel}", - "xpack.maps.lens.groupLabel": "マップ", "xpack.maps.loadMap.errorAttemptingToLoadSavedMap": "マップを読み込めません", "xpack.maps.logDatatable.region": "リージョンキー", "xpack.maps.logDatatable.value": "値", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 455314827fd7e..79b6f4e923351 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -25142,7 +25142,6 @@ "xpack.lens.configPanel.selectAnnotationMethod": "选择标注方法", "xpack.lens.configPanel.selectLayerType": "选择图层类型", "xpack.lens.configPanel.selectVisualization": "选择可视化", - "xpack.lens.configPanel.visualizationType": "可视化类型", "xpack.lens.configure.emptyConfig": "添加或拖放字段", "xpack.lens.configure.invalidBottomReferenceLineDimension": "此参考线分配给了不再存在或不再有效的轴。您可以将此参考线移到其他可用的轴,或将其移除。", "xpack.lens.configure.invalidConfigTooltip": "配置无效。", @@ -25164,7 +25163,6 @@ "xpack.lens.datatable.column.help": "数据表列", "xpack.lens.datatable.conjunctionSign": " & ", "xpack.lens.datatable.expressionHelpLabel": "数据表呈现器", - "xpack.lens.datatable.groupLabel": "表格", "xpack.lens.datatable.headingLabel": "值", "xpack.lens.datatable.label": "表", "xpack.lens.datatable.metric": "指标", @@ -25303,7 +25301,6 @@ "xpack.lens.guageVisualization.chartCannotRenderMinGreaterMax": "最小值不能大于最大值", "xpack.lens.heatmap.addLayer": "可视化", "xpack.lens.heatmap.cellValueLabel": "单元格值", - "xpack.lens.heatmap.groupLabel": "热图", "xpack.lens.heatmap.headingLabel": "值", "xpack.lens.heatmap.heatmapLabel": "热图", "xpack.lens.heatmap.horizontalAxisDisabledHelpText": "此设置仅在启用水平轴时适用。", @@ -25313,7 +25310,6 @@ "xpack.lens.heatmapChart.legendVisibility.hide": "隐藏", "xpack.lens.heatmapChart.legendVisibility.show": "显示", "xpack.lens.heatmapVisualization.arrayValuesWarningMessage": "{label} 包含数组值。您的可视化可能无法正常渲染。", - "xpack.lens.heatmapVisualization.heatmapGroupLabel": "级别", "xpack.lens.heatmapVisualization.heatmapLabel": "热图", "xpack.lens.heatmapVisualization.missingXAccessorLongMessage": "水平轴配置缺失。", "xpack.lens.heatmapVisualization.missingXAccessorShortMessage": "缺失水平轴。", @@ -25703,7 +25699,6 @@ "xpack.lens.layerActions.layerSettingsAction": "图层设置", "xpack.lens.layerPanel.ignoreGlobalFilters": "忽略全局筛选", "xpack.lens.layerPanel.missingDataView": "找不到数据视图", - "xpack.lens.layerPanel.selectVisualizationType": "选择可视化类型", "xpack.lens.layerSettings.ignoreGlobalFilters": "使用全局筛选", "xpack.lens.layerTitle.fallbackLabel": "图层", "xpack.lens.legacyMetric.addLayer": "可视化", @@ -25714,7 +25709,6 @@ "xpack.lens.legacyMetric.dynamicColoring.label": "按值上色", "xpack.lens.legacyMetric.dynamicColoring.none": "无", "xpack.lens.legacyMetric.dynamicColoring.text": "文本", - "xpack.lens.legacyMetric.groupLabel": "目标值和单值", "xpack.lens.legacyMetric.label": "旧版指标", "xpack.lens.legacyMetric.metricSize.extraLarge": "XL", "xpack.lens.legacyMetric.metricSize.extraSmall": "XS", @@ -25747,7 +25741,6 @@ "xpack.lens.metric.color": "颜色", "xpack.lens.metric.colorMode.dynamic": "动态", "xpack.lens.metric.colorMode.static": "静态", - "xpack.lens.metric.groupLabel": "目标值和单值", "xpack.lens.metric.headingLabel": "值", "xpack.lens.metric.icon": "图标装饰", "xpack.lens.metric.iconSelect.computeLabel": "计算", @@ -25796,8 +25789,6 @@ "xpack.lens.pie.addLayer": "可视化", "xpack.lens.pie.arrayValues": "以下维度包含数组值:{label}。您的可视化可能无法正常渲染。", "xpack.lens.pie.collapsedDimensionsDontCount": "(折叠的维度不计入此限制。)", - "xpack.lens.pie.donutLabel": "圆环图", - "xpack.lens.pie.groupLabel": "比例", "xpack.lens.pie.groupMetricLabel": "指标", "xpack.lens.pie.groupMetricLabelSingular": "指标", "xpack.lens.pie.headingLabel": "值", @@ -25821,29 +25812,17 @@ "xpack.lens.pie.verticalAxisLabel": "垂直轴", "xpack.lens.pie.wafflelabel": "华夫饼图", "xpack.lens.pie.waffleSuggestionLabel": "华夫饼图", - "xpack.lens.pieChart.categoriesInLegendLabel": "隐藏标签", "xpack.lens.pieChart.colorPicker.disabledBecauseGroupBy": "图层包括一个或多个“分组依据”维度时,无法将定制颜色应用于单个切片。", "xpack.lens.pieChart.colorPicker.disabledBecauseSliceBy": "图层包括一个或多个“切片依据”维度时,无法将定制颜色应用于单个切片。", - "xpack.lens.pieChart.emptySizeRatioLabel": "内部面积大小", "xpack.lens.pieChart.emptySizeRatioOptions.large": "大", "xpack.lens.pieChart.emptySizeRatioOptions.medium": "中", "xpack.lens.pieChart.emptySizeRatioOptions.small": "小", - "xpack.lens.pieChart.fitInsideOnlyLabel": "仅内部", - "xpack.lens.pieChart.hiddenNumbersLabel": "在图表中隐藏", - "xpack.lens.pieChart.labelPositionLabel": "位置", "xpack.lens.pieChart.legendVisibility.auto": "自动", "xpack.lens.pieChart.legendVisibility.hide": "隐藏", "xpack.lens.pieChart.legendVisibility.show": "显示", "xpack.lens.pieChart.multipleMetrics": "多个指标", "xpack.lens.pieChart.nestedLegendLabel": "嵌套", - "xpack.lens.pieChart.numberLabels": "值", - "xpack.lens.pieChart.percentDecimalsLabel": "百分比的最大小数位数", - "xpack.lens.pieChart.showCategoriesLabel": "内部或外部", - "xpack.lens.pieChart.showFormatterValuesLabel": "显示值", - "xpack.lens.pieChart.showPercentValuesLabel": "显示百分比", "xpack.lens.pieChart.showTreemapCategoriesLabel": "显示标签", - "xpack.lens.pieChart.valuesLabel": "标签", - "xpack.lens.pieChart.visualOptionsLabel": "视觉选项", "xpack.lens.primaryMetric.headingLabel": "值", "xpack.lens.primaryMetric.label": "主要指标", "xpack.lens.reducedTimeRangeSuffix": "上一个 {reducedTimeRange}", @@ -25861,10 +25840,7 @@ "xpack.lens.share.csvButton": "下载 CSV", "xpack.lens.share.export": "导出文件", "xpack.lens.share.helpText": "导出此可视化的 CSV。", - "xpack.lens.shared.AppearanceLabel": "外观", "xpack.lens.shared.axisNameLabel": "轴标题", - "xpack.lens.shared.chartValueLabelVisibilityLabel": "标签", - "xpack.lens.shared.chartValueLabelVisibilityTooltip": "如果没有足够的空间,可能会隐藏值标签", "xpack.lens.shared.labelTruncation": "标签截断", "xpack.lens.shared.Lagend ": "序列标题", "xpack.lens.shared.legendAlignmentLabel": "对齐方式", @@ -25925,7 +25901,6 @@ "xpack.lens.shared.legendValues.varianceDesc": "序列中所有值的方差。", "xpack.lens.shared.legendVisibilityLabel": "图层可见性的缩放范围", "xpack.lens.shared.maxLinesLabel": "行限制", - "xpack.lens.shared.metric.appearanceLabel": "外观", "xpack.lens.shared.nestedLegendLabel": "嵌套", "xpack.lens.shared.overwriteAxisTitle": "覆盖轴标题", "xpack.lens.shared.overwriteLegendTitle": "覆盖序列标题", @@ -25934,7 +25909,6 @@ "xpack.lens.shared.ticksPositionOptionsTooltip": "将刻度放在每个带边框上,而不是平均分布", "xpack.lens.shared.valueInLegendLabel": "显示值", "xpack.lens.shared.valueLabelsVisibility.auto": "隐藏", - "xpack.lens.shared.valueLabelsVisibility.inside": "显示", "xpack.lens.staticValue.headingLabel": "位置", "xpack.lens.sugegstion.refreshSuggestionLabel": "刷新", "xpack.lens.suggestion.refreshSuggestionTooltip": "基于选定可视化刷新建议。", @@ -26053,7 +26027,6 @@ "xpack.lens.xyChart.fill.below": "之下", "xpack.lens.xyChart.fill.label": "填充", "xpack.lens.xyChart.fill.none": "无", - "xpack.lens.xyChart.fillOpacityLabel": "填充透明度", "xpack.lens.xyChart.Gridlines": "网格线", "xpack.lens.xyChart.horizontalAxisLabel": "水平轴", "xpack.lens.xyChart.horizontalLeftAxisLabel": "水平顶轴", @@ -26118,7 +26091,6 @@ "xpack.lens.xySuggestions.yAxixConjunctionSign": " & ", "xpack.lens.xyVisualization.areaLabel": "面积图", "xpack.lens.xyVisualization.arrayValues": "{label} 包含数组值。您的可视化可能无法正常渲染。", - "xpack.lens.xyVisualization.barGroupLabel": "条形图", "xpack.lens.xyVisualization.barHorizontalFullLabel": "水平条形图", "xpack.lens.xyVisualization.barHorizontalLabel": "水平条形图", "xpack.lens.xyVisualization.barLabel": "垂直条形图", @@ -26130,7 +26102,6 @@ "xpack.lens.xyVisualization.dataTypeFailureXShort": "{axis} 的数据类型错误。", "xpack.lens.xyVisualization.dataTypeFailureYLong": "为 {axis} 提供的维度 {label} 具有错误的数据类型。应为数字,但却为 {dataType}", "xpack.lens.xyVisualization.dataTypeFailureYShort": "{axis} 的数据类型错误。", - "xpack.lens.xyVisualization.lineGroupLabel": "折线图和面积图", "xpack.lens.xyVisualization.lineLabel": "折线图", "xpack.lens.xyVisualization.mixedBarHorizontalLabel": "水平混合条形图", "xpack.lens.xyVisualization.mixedLabel": "混合 XY", @@ -26725,10 +26696,7 @@ "xpack.maps.lens.choropleth.label": "区域地图", "xpack.maps.lens.choroplethChart.addLayer": "添加可视化图层", "xpack.maps.lens.choroplethChart.choroplethLayerLabel": "{emsLayerLabel}(按 {accessorLabel})", - "xpack.maps.lens.choroplethChart.metricValueLabel": "指标", - "xpack.maps.lens.choroplethChart.regionKeyLabel": "区域密钥", "xpack.maps.lens.choroplethChart.suggestionLabel": "{emsLayerLabel}(按 {metricLabel})", - "xpack.maps.lens.groupLabel": "地图", "xpack.maps.loadMap.errorAttemptingToLoadSavedMap": "无法加载地图", "xpack.maps.logDatatable.region": "区域密钥", "xpack.maps.logDatatable.value": "值", diff --git a/x-pack/test/functional/apps/dashboard/group1/feature_controls/time_to_visualize_security.ts b/x-pack/test/functional/apps/dashboard/group1/feature_controls/time_to_visualize_security.ts index 7b36b19c32da8..07dcf1ac03182 100644 --- a/x-pack/test/functional/apps/dashboard/group1/feature_controls/time_to_visualize_security.ts +++ b/x-pack/test/functional/apps/dashboard/group1/feature_controls/time_to_visualize_security.ts @@ -103,7 +103,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await PageObjects.dashboard.waitForRenderComplete(); await dashboardPanelActions.openContextMenu(); await dashboardPanelActions.clickEdit(); - await PageObjects.lens.switchToVisualization('donut'); + await PageObjects.lens.switchToVisualization('pie'); await PageObjects.lens.saveAndReturn(); await PageObjects.dashboard.waitForRenderComplete(); diff --git a/x-pack/test/functional/apps/dashboard/group2/dashboard_lens_by_value.ts b/x-pack/test/functional/apps/dashboard/group2/dashboard_lens_by_value.ts index 423c9819a048f..19d44ba6a6035 100644 --- a/x-pack/test/functional/apps/dashboard/group2/dashboard_lens_by_value.ts +++ b/x-pack/test/functional/apps/dashboard/group2/dashboard_lens_by_value.ts @@ -44,7 +44,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await PageObjects.dashboard.waitForRenderComplete(); await dashboardPanelActions.openContextMenu(); await dashboardPanelActions.clickEdit(); - await PageObjects.lens.switchToVisualization('donut'); + await PageObjects.lens.switchToVisualization('pie'); await PageObjects.lens.saveAndReturn(); await PageObjects.dashboard.waitForRenderComplete(); diff --git a/x-pack/test/functional/apps/discover/visualize_field.ts b/x-pack/test/functional/apps/discover/visualize_field.ts index 13df0fe667357..eca02426e06d1 100644 --- a/x-pack/test/functional/apps/discover/visualize_field.ts +++ b/x-pack/test/functional/apps/discover/visualize_field.ts @@ -153,7 +153,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { expect(await testSubjects.exists('unifiedHistogramChart')).to.be(true); expect(await testSubjects.exists('xyVisChart')).to.be(true); - await PageObjects.discover.chooseLensSuggestion('donut'); + await PageObjects.discover.chooseLensSuggestion('pie'); await PageObjects.header.waitUntilLoadingHasFinished(); expect(await testSubjects.exists('partitionVisChart')).to.be(true); }); @@ -288,9 +288,9 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { keepOpen: true, }); await testSubjects.click('lns-indexPattern-dimensionContainerBack'); - // click donut from suggestions + // click pie from suggestions await testSubjects.click('lensSuggestionsPanelToggleButton'); - await testSubjects.click('lnsSuggestion-donut'); + await testSubjects.click('lnsSuggestion-pie'); expect(await testSubjects.exists('partitionVisChart')).to.be(true); }); diff --git a/x-pack/test/functional/apps/lens/group1/smokescreen.ts b/x-pack/test/functional/apps/lens/group1/smokescreen.ts index befed343f1295..bf02959b08b8f 100644 --- a/x-pack/test/functional/apps/lens/group1/smokescreen.ts +++ b/x-pack/test/functional/apps/lens/group1/smokescreen.ts @@ -45,7 +45,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.switchToVisualization('lnsDatatable'); await PageObjects.lens.removeDimension('lnsDatatable_rows'); - await PageObjects.lens.switchToVisualization('bar_stacked'); + await PageObjects.lens.switchToVisualization('area'); await PageObjects.lens.configureDimension({ dimension: 'lnsXY_splitDimensionPanel > lns-empty-dimension', @@ -128,7 +128,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(await PageObjects.lens.getLayerType(0)).to.eql('Line'); // expect first layer to be line, second layer to be bar chart - expect(await PageObjects.lens.getLayerType(1)).to.eql('Bar vertical stacked'); + expect(await PageObjects.lens.getLayerType(1)).to.eql('Bar'); await PageObjects.lens.configureDimension({ dimension: 'lns-layerPanel-1 > lnsXY_xDimensionPanel > lns-empty-dimension', operation: 'terms', @@ -165,7 +165,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); await PageObjects.lens.createLayer('data', undefined, 'bar'); - expect(await PageObjects.lens.getLayerType(1)).to.eql('Bar vertical'); + expect(await PageObjects.lens.getLayerType(1)).to.eql('Bar'); await PageObjects.lens.configureDimension({ dimension: 'lns-layerPanel-1 > lnsXY_xDimensionPanel > lns-empty-dimension', @@ -181,14 +181,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // only changes one layer for compatible chart await PageObjects.lens.switchToVisualization('line', undefined, 1); - expect(await PageObjects.lens.getLayerType(0)).to.eql('Bar vertical stacked'); + expect(await PageObjects.lens.getLayerType(0)).to.eql('Bar'); expect(await PageObjects.lens.getLayerType(1)).to.eql('Line'); - // changes all layers for multilayer chart - await PageObjects.lens.switchToVisualization('bar_horizontal_stacked', undefined, 0); - expect(await PageObjects.lens.getLayerType(0)).to.eql('Bar horizontal stacked'); - expect(await PageObjects.lens.getLayerType(1)).to.eql('Bar horizontal stacked'); - // generates new one layer chart based on selected layer await PageObjects.lens.switchToVisualization('pie', undefined, 1); expect(await PageObjects.lens.getLayerType(0)).to.eql('Pie'); @@ -308,18 +303,11 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should show value labels on bar charts when enabled', async () => { // enable value labels - await PageObjects.lens.openVisualOptions(); + await PageObjects.lens.openTextOptions(); await testSubjects.click('lns_valueLabels_inside'); // check for value labels - let data = await PageObjects.lens.getCurrentChartDebugState('xyVisChart'); - expect(data?.bars?.[0].labels).not.to.eql(0); - - // switch to stacked bar chart - await PageObjects.lens.switchToVisualization('bar_stacked'); - - // check for value labels - data = await PageObjects.lens.getCurrentChartDebugState('xyVisChart'); + const data = await PageObjects.lens.getCurrentChartDebugState('xyVisChart'); expect(data?.bars?.[0].labels).not.to.eql(0); }); @@ -383,13 +371,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ); }); - it('should transition from line chart to donut chart and to bar chart', async () => { + it('should transition from line chart to pie chart and to bar chart', async () => { await PageObjects.visualize.gotoVisualizationLandingPage(); await listingTable.searchForItemWithName('lnsXYvis'); await PageObjects.lens.clickVisualizeListItemTitle('lnsXYvis'); await PageObjects.lens.goToTimeRange(); - expect(await PageObjects.lens.hasChartSwitchWarning('donut')).to.eql(true); - await PageObjects.lens.switchToVisualization('donut'); + expect(await PageObjects.lens.hasChartSwitchWarning('pie')).to.eql(true); + await PageObjects.lens.switchToVisualization('pie'); expect(await PageObjects.lens.getTitle()).to.eql('lnsXYvis'); expect(await PageObjects.lens.getDimensionTriggerText('lnsPie_sliceByDimensionPanel')).to.eql( @@ -402,7 +390,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(await PageObjects.lens.hasChartSwitchWarning('bar')).to.eql(false); await PageObjects.lens.switchToVisualization('bar'); expect(await PageObjects.lens.getTitle()).to.eql('lnsXYvis'); - expect(await PageObjects.lens.getDimensionTriggerText('lnsXY_xDimensionPanel')).to.eql( + expect(await PageObjects.lens.getDimensionTriggerText('lnsXY_splitDimensionPanel')).to.eql( 'Top 3 values of ip' ); expect(await PageObjects.lens.getDimensionTriggerText('lnsXY_yDimensionPanel')).to.eql( @@ -798,10 +786,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await filterBar.removeFilter('extension.raw'); }); - it('should show visual options button group for a donut chart', async () => { + it('should show visual options button group for a pie chart', async () => { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickVisType('lens'); - await PageObjects.lens.switchToVisualization('donut'); + await PageObjects.lens.switchToVisualization('pie'); const hasVisualOptionsButton = await PageObjects.lens.hasVisualOptionsButton(); expect(hasVisualOptionsButton).to.be(true); @@ -812,15 +800,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); - it('should not show visual options button group for a pie chart', async () => { - await PageObjects.visualize.navigateToNewVisualization(); - await PageObjects.visualize.clickVisType('lens'); - await PageObjects.lens.switchToVisualization('pie'); - - const hasVisualOptionsButton = await PageObjects.lens.hasVisualOptionsButton(); - expect(hasVisualOptionsButton).to.be(false); - }); - it('should allow edit meta-data for Lens chart on listing page', async () => { await PageObjects.visualize.gotoVisualizationLandingPage(); await listingTable.searchForItemWithName('Afancilenstest'); diff --git a/x-pack/test/functional/apps/lens/group2/partition.ts b/x-pack/test/functional/apps/lens/group2/partition.ts index 45c4d1b1dfda0..cb4894866c23f 100644 --- a/x-pack/test/functional/apps/lens/group2/partition.ts +++ b/x-pack/test/functional/apps/lens/group2/partition.ts @@ -49,7 +49,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('should switch to donut charts keeping all dimensions', async () => { - await PageObjects.lens.switchToVisualization('donut'); + await PageObjects.lens.setDonutHoleSize('Large'); expect( await testSubjects.exists('lnsPie_sliceByDimensionPanel > lns-empty-dimension') diff --git a/x-pack/test/functional/apps/lens/group4/chart_data.ts b/x-pack/test/functional/apps/lens/group4/chart_data.ts index 9d43abd4ac650..2b15d11b1125e 100644 --- a/x-pack/test/functional/apps/lens/group4/chart_data.ts +++ b/x-pack/test/functional/apps/lens/group4/chart_data.ts @@ -84,7 +84,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('should render donut chart', async () => { - await PageObjects.lens.switchToVisualization('donut'); + await PageObjects.lens.setDonutHoleSize('Large'); const data = await PageObjects.lens.getCurrentChartDebugState('partitionVisChart'); assertMatchesExpectedPieData(data); }); diff --git a/x-pack/test/functional/apps/lens/group4/colors.ts b/x-pack/test/functional/apps/lens/group4/colors.ts index 2670cc19653db..5faf6e05bbcd5 100644 --- a/x-pack/test/functional/apps/lens/group4/colors.ts +++ b/x-pack/test/functional/apps/lens/group4/colors.ts @@ -68,7 +68,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('should carry over palette to the pie chart', async () => { - await PageObjects.lens.switchToVisualization('donut'); + await PageObjects.lens.switchToVisualization('pie'); await PageObjects.lens.openDimensionEditor( 'lnsPie_sliceByDimensionPanel > lns-dimensionTrigger' ); diff --git a/x-pack/test/functional/apps/lens/group5/gauge.ts b/x-pack/test/functional/apps/lens/group5/gauge.ts index bbe71da7bb2f6..20980857f9d1a 100644 --- a/x-pack/test/functional/apps/lens/group5/gauge.ts +++ b/x-pack/test/functional/apps/lens/group5/gauge.ts @@ -7,20 +7,19 @@ import { BulletSubtype } from '@elastic/charts'; import expect from '@kbn/expect'; -import { GaugeShapes } from '@kbn/visualizations-plugin/common'; import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { - const PageObjects = getPageObjects(['visualize', 'lens', 'common', 'header']); + const PageObjects = getPageObjects(['visualize', 'lens', 'common', 'header', 'timePicker']); const elasticChart = getService('elasticChart'); const testSubjects = getService('testSubjects'); describe('lens gauge', () => { before(async () => { + await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickVisType('lens'); await elasticChart.setNewChartUiDebugFlag(true); - await PageObjects.lens.goToTimeRange(); await PageObjects.lens.configureDimension({ dimension: 'lnsXY_xDimensionPanel > lns-empty-dimension', @@ -37,7 +36,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('should switch to gauge and render a gauge with default values', async () => { - await PageObjects.lens.switchToVisualization(GaugeShapes.HORIZONTAL_BULLET, 'horizontal'); + await PageObjects.lens.switchToVisualization('lnsGauge', 'gauge'); await PageObjects.lens.waitForVisualization('gaugeChart'); const { bullet } = await elasticChart.getChartDebugData(); const debugData = bullet?.rows[0][0]; @@ -48,7 +47,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('should reflect edits for gauge', async () => { - await PageObjects.lens.switchToVisualization(GaugeShapes.HORIZONTAL_BULLET, 'horizontal'); + await PageObjects.lens.switchToVisualization('lnsGauge', 'gauge'); await PageObjects.lens.waitForVisualization('gaugeChart'); await PageObjects.lens.configureDimension({ dimension: 'lnsGauge_metricDimensionPanel > lns-dimensionTrigger', @@ -61,7 +60,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.setEuiSwitch('lnsDynamicColoringGaugeSwitch', 'check'); await PageObjects.lens.closeDimensionEditor(); - await PageObjects.lens.openVisualOptions(); + await PageObjects.lens.openTextOptions(); await PageObjects.lens.retrySetValue('lnsToolbarGaugeLabelMajor', 'custom title'); await PageObjects.lens.retrySetValue('lnsToolbarGaugeLabelMinor-select', 'custom', {}); await PageObjects.lens.retrySetValue('lnsToolbarGaugeLabelMinor', 'custom subtitle'); @@ -97,7 +96,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(debugData?.domain).to.eql([1000, 25000]); }); it('should seamlessly switch to vertical bullet chart without losing configuration', async () => { - await PageObjects.lens.switchToVisualization(GaugeShapes.VERTICAL_BULLET, 'vertical'); + await PageObjects.lens.openVisualOptions(); + await testSubjects.click('lns_gaugeOrientation_verticalBullet'); const { bullet } = await elasticChart.getChartDebugData(); const debugData = bullet?.rows[0][0]; expect(debugData?.subtype).to.be(BulletSubtype.vertical); @@ -107,8 +107,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(debugData?.target).to.be(11250); expect(debugData?.domain).to.eql([1000, 25000]); }); - it('should seamlessly switch to semi-circular gauge chart without losing configuration', async () => { - await PageObjects.lens.switchToVisualization(GaugeShapes.SEMI_CIRCLE, 'semi'); + it('should seamlessly switch to minor arc gauge chart without losing configuration', async () => { + await PageObjects.lens.openVisualOptions(); + await PageObjects.lens.setGaugeShape('Minor arc'); const { bullet } = await elasticChart.getChartDebugData(); const debugData = bullet?.rows[0][0]; expect(debugData?.subtype).to.be(BulletSubtype.halfCircle); @@ -119,7 +120,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(debugData?.domain).to.eql([1000, 25000]); }); it('should seamlessly switch to arc gauge chart without losing configuration', async () => { - await PageObjects.lens.switchToVisualization(GaugeShapes.ARC, 'arc'); + await PageObjects.lens.openVisualOptions(); + await PageObjects.lens.setGaugeShape('Major arc'); const { bullet } = await elasticChart.getChartDebugData(); const debugData = bullet?.rows[0][0]; expect(debugData?.subtype).to.be(BulletSubtype.twoThirdsCircle); @@ -130,7 +132,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(debugData?.domain).to.eql([1000, 25000]); }); it('should seamlessly switch to circular gauge chart without losing configuration', async () => { - await PageObjects.lens.switchToVisualization(GaugeShapes.CIRCLE, 'circular'); + await PageObjects.lens.openVisualOptions(); + await PageObjects.lens.setGaugeShape('Circle'); const { bullet } = await elasticChart.getChartDebugData(); const debugData = bullet?.rows[0][0]; expect(debugData?.subtype).to.be(BulletSubtype.circle); diff --git a/x-pack/test/functional/apps/lens/group6/error_handling.ts b/x-pack/test/functional/apps/lens/group6/error_handling.ts index 83acbff338df2..8bc7432601bd1 100644 --- a/x-pack/test/functional/apps/lens/group6/error_handling.ts +++ b/x-pack/test/functional/apps/lens/group6/error_handling.ts @@ -55,7 +55,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.waitForMissingDataViewWarning(); await PageObjects.lens.switchToVisualization('lnsDatatable'); await PageObjects.lens.waitForMissingDataViewWarning(); - await PageObjects.lens.switchToVisualization('donut'); + await PageObjects.lens.switchToVisualization('pie'); await PageObjects.lens.waitForMissingDataViewWarning(); await PageObjects.lens.switchToVisualization('line'); await PageObjects.lens.waitForMissingDataViewWarning(); diff --git a/x-pack/test/functional/apps/lens/group6/workspace_size.ts b/x-pack/test/functional/apps/lens/group6/workspace_size.ts index 4201e523d03ae..e7b9c7831b3af 100644 --- a/x-pack/test/functional/apps/lens/group6/workspace_size.ts +++ b/x-pack/test/functional/apps/lens/group6/workspace_size.ts @@ -6,7 +6,6 @@ */ import expect from '@kbn/expect'; -import { GaugeShapes } from '@kbn/visualizations-plugin/common'; import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { @@ -31,7 +30,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const DEFAULT_WINDOW_SIZE = [1400, 900]; const VERTICAL_16_9 = 16 / 9; - const outerWorkspaceDimensions = { width: 700, height: 400 }; + const outerWorkspaceDimensions = { width: 704, height: 410 }; let UNCONSTRAINED = outerWorkspaceDimensions.width / outerWorkspaceDimensions.height; before(async () => { @@ -174,12 +173,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expectedWidth: '300px', expectedHeight: '300px', }, - { id: 'donut', aspectRatio: UNCONSTRAINED }, - { - id: 'lnsMetric', - expectedWidth: '300px', - expectedHeight: '300px', - }, { id: 'mosaic', aspectRatio: UNCONSTRAINED }, { id: 'lnsMetric', @@ -250,16 +243,16 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('gauge size (absolute pixels) - horizontal', async () => { await retry.try(async () => { - await PageObjects.lens.switchToVisualization(GaugeShapes.HORIZONTAL_BULLET, 'horizontal'); + await PageObjects.lens.switchToVisualization('lnsGauge', 'gauge'); + await PageObjects.lens.waitForVisualization('gaugeChart'); }); await assertWorkspaceDimensions('600px', '200px'); }); it('gauge size (absolute pixels) - vertical', async () => { - await retry.try(async () => { - await PageObjects.lens.switchToVisualization(GaugeShapes.VERTICAL_BULLET, 'vertical'); - }); + await PageObjects.lens.openVisualOptions(); + await testSubjects.click('lns_gaugeOrientation_verticalBullet'); // this height is below the requested 600px // that is because the window size isn't large enough to fit the requested dimensions @@ -270,23 +263,20 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('gauge size (absolute pixels) - arc', async () => { - await retry.try(async () => { - await PageObjects.lens.switchToVisualization(GaugeShapes.SEMI_CIRCLE, 'semi'); - }); + await PageObjects.lens.openVisualOptions(); + await PageObjects.lens.setGaugeShape('Minor arc'); await assertWorkspaceDimensions('600px', '375px'); }); it('gauge size (absolute pixels) - major arc', async () => { - await retry.try(async () => { - await PageObjects.lens.switchToVisualization(GaugeShapes.ARC, 'arc'); - }); + await PageObjects.lens.openVisualOptions(); + await PageObjects.lens.setGaugeShape('Major arc'); await assertWorkspaceDimensions('600px', '430px'); }); it('gauge size (absolute pixels) - circle', async () => { - await retry.try(async () => { - await PageObjects.lens.switchToVisualization(GaugeShapes.CIRCLE, 'circular'); - }); + await PageObjects.lens.openVisualOptions(); + await PageObjects.lens.setGaugeShape('Circle'); await assertWorkspaceDimensions('600px', '430px'); }); @@ -322,7 +312,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await assertWorkspaceAspectRatio(VERTICAL_16_9); await retry.try(async () => { - await PageObjects.lens.switchToVisualization('bar_horizontal_stacked'); + await PageObjects.lens.switchToVisualization('bar'); }); await assertWorkspaceAspectRatio(UNCONSTRAINED); diff --git a/x-pack/test/functional/apps/lens/open_in_lens/agg_based/pie.ts b/x-pack/test/functional/apps/lens/open_in_lens/agg_based/pie.ts index 9fcdef35239fa..5d075a5bca88b 100644 --- a/x-pack/test/functional/apps/lens/open_in_lens/agg_based/pie.ts +++ b/x-pack/test/functional/apps/lens/open_in_lens/agg_based/pie.ts @@ -124,7 +124,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { let chartSwitcher = await testSubjects.find('lnsChartSwitchPopover'); let type = await chartSwitcher.getVisibleText(); - expect(type).to.be('Donut'); + expect(type).to.be('Pie'); + expect(await lens.getDonutHoleSize()).to.equal('Small'); const goBackBtn = await testSubjects.find('lnsApp_goBackToAppButton'); await goBackBtn.click(); @@ -141,6 +142,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { chartSwitcher = await testSubjects.find('lnsChartSwitchPopover'); type = await chartSwitcher.getVisibleText(); expect(type).to.be('Pie'); + expect(await lens.getDonutHoleSize()).to.equal('None'); }); }); } diff --git a/x-pack/test/functional/apps/lens/open_in_lens/agg_based/xy.ts b/x-pack/test/functional/apps/lens/open_in_lens/agg_based/xy.ts index b54ea10d2217a..c615bb9a1b52f 100644 --- a/x-pack/test/functional/apps/lens/open_in_lens/agg_based/xy.ts +++ b/x-pack/test/functional/apps/lens/open_in_lens/agg_based/xy.ts @@ -126,7 +126,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const layerChartSwitches = await testSubjects.findAll('lnsChartSwitchPopover'); expect(layerChartSwitches.length).to.be(2); expect(await layerChartSwitches[0].getVisibleText()).to.be('Area'); - expect(await layerChartSwitches[1].getVisibleText()).to.be('Bar vertical'); + expect(await layerChartSwitches[1].getVisibleText()).to.be('Bar'); const yDimensionText1 = await lens.getDimensionTriggerText('lnsXY_yDimensionPanel', 0); const yDimensionText2 = await lens.getDimensionTriggerText('lnsXY_yDimensionPanel', 1); expect(yDimensionText1).to.be('Count'); @@ -152,7 +152,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { expect(await lens.getLayerCount()).to.be(1); const layerChartSwitches = await testSubjects.findAll('lnsChartSwitchPopover'); expect(layerChartSwitches.length).to.be(1); - expect(await layerChartSwitches[0].getVisibleText()).to.be('Bar vertical'); + expect(await layerChartSwitches[0].getVisibleText()).to.be('Bar'); const yDimensionText1 = await lens.getDimensionTriggerText('lnsXY_yDimensionPanel', 0); const yDimensionText2 = await lens.getDimensionTriggerText('lnsXY_yDimensionPanel', 1); expect(yDimensionText1).to.be('Count'); @@ -238,7 +238,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { expect(await lens.getLayerCount()).to.be(1); const layerChartSwitches = await testSubjects.findAll('lnsChartSwitchPopover'); expect(layerChartSwitches.length).to.be(1); - expect(await layerChartSwitches[0].getVisibleText()).to.be('Area stacked'); + expect(await layerChartSwitches[0].getVisibleText()).to.be('Area'); }); }); @@ -258,7 +258,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { expect(await lens.getLayerCount()).to.be(1); const layerChartSwitches = await testSubjects.findAll('lnsChartSwitchPopover'); expect(layerChartSwitches.length).to.be(1); - expect(await layerChartSwitches[0].getVisibleText()).to.be('Area percentage'); + expect(await layerChartSwitches[0].getVisibleText()).to.be('Area'); }); }); @@ -277,7 +277,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { expect(await lens.getLayerCount()).to.be(1); const layerChartSwitches = await testSubjects.findAll('lnsChartSwitchPopover'); expect(layerChartSwitches.length).to.be(1); - expect(await layerChartSwitches[0].getVisibleText()).to.be('Bar horizontal'); + expect(await layerChartSwitches[0].getVisibleText()).to.be('Bar'); + expect(await lens.getSelectedBarOrientationSetting()).to.be('Horizontal'); }); }); diff --git a/x-pack/test/functional/apps/lens/open_in_lens/tsvb/top_n.ts b/x-pack/test/functional/apps/lens/open_in_lens/tsvb/top_n.ts index 5207b18035837..26afd4594b852 100644 --- a/x-pack/test/functional/apps/lens/open_in_lens/tsvb/top_n.ts +++ b/x-pack/test/functional/apps/lens/open_in_lens/tsvb/top_n.ts @@ -78,7 +78,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await lens.waitForVisualization('xyVisChart'); const chartSwitcher = await testSubjects.find('lnsChartSwitchPopover'); const type = await chartSwitcher.getVisibleText(); - expect(type).to.be('Bar horizontal'); + expect(type).to.be('Bar'); await retry.try(async () => { const layerCount = await lens.getLayerCount(); expect(layerCount).to.be(1); diff --git a/x-pack/test/functional/page_objects/lens_page.ts b/x-pack/test/functional/page_objects/lens_page.ts index 4c32a87a57ba3..5c54fc99128f6 100644 --- a/x-pack/test/functional/page_objects/lens_page.ts +++ b/x-pack/test/functional/page_objects/lens_page.ts @@ -838,9 +838,21 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont return testSubjects.exists('lnsVisualOptionsButton'); }, async openVisualOptions() { + if (await testSubjects.exists('lnsVisualOptionsPopover_title', { timeout: 50 })) { + return; + } await retry.try(async () => { await testSubjects.click('lnsVisualOptionsButton'); - await testSubjects.exists('lnsVisualOptionsButton'); + await testSubjects.exists('lnsVisualOptionsPopover_title'); + }); + }, + async openTextOptions() { + if (await testSubjects.exists('lnsTextOptionsPopover_title', { timeout: 50 })) { + return; + } + await retry.try(async () => { + await testSubjects.click('lnsTextOptionsButton'); + await testSubjects.exists('lnsTextOptionsPopover_title'); }); }, async retrySetValue( @@ -885,7 +897,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont * Uses the Lens visualization switcher to switch visualizations. * * @param subVisualizationId - the ID of the sub-visualization to switch to, such as - * lnsDatatable or bar_stacked + * lnsDatatable or bar */ async switchToVisualization(subVisualizationId: string, searchTerm?: string, layerIndex = 0) { await this.openChartSwitchPopover(layerIndex); @@ -933,6 +945,58 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont } }, + async getDonutHoleSize() { + await this.openVisualOptions(); + const comboboxOptions = await comboBox.getComboBoxSelectedOptions('lnsEmptySizeRatioOption'); + return comboboxOptions[0]; + }, + + async setDonutHoleSize(value: string) { + await retry.waitFor('visual options toolbar is open', async () => { + await this.openVisualOptions(); + return await testSubjects.exists('lnsEmptySizeRatioOption'); + }); + await comboBox.set('lnsEmptySizeRatioOption', value); + }, + + async setGaugeShape(value: string) { + await retry.waitFor('visual options toolbar is open', async () => { + await this.openVisualOptions(); + return await testSubjects.exists('lnsToolbarGaugeAngleType'); + }); + await comboBox.set('lnsToolbarGaugeAngleType > comboBoxInput', value); + }, + + async getSelectedBarOrientationSetting() { + await retry.waitFor('visual options are displayed', async () => { + await this.openVisualOptions(); + return await testSubjects.exists('lns_barOrientation'); + }); + const orientationButtons = await find.allByCssSelector( + `[data-test-subj^="lns_barOrientation_"]` + ); + for (const button of orientationButtons) { + const ariaPressed = await button.getAttribute('aria-pressed'); + const isSelected = ariaPressed === 'true'; + if (isSelected) { + return button?.getVisibleText(); + } + } + }, + + async getGaugeOrientationSetting() { + const orientationButtons = await find.allByCssSelector( + `[data-test-subj^="lns_gaugeOrientation_"]` + ); + for (const button of orientationButtons) { + const ariaPressed = await button.getAttribute('aria-pressed'); + const isSelected = ariaPressed === 'true'; + if (isSelected) { + return button?.getVisibleText(); + } + } + }, + /** Counts the visible warnings in the config panel */ async getWorkspaceErrorCount() { const workspaceErrorsExists = await testSubjects.exists('lnsWorkspaceErrors'); @@ -967,7 +1031,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont * Checks a specific subvisualization in the chart switcher for a "data loss" indicator * * @param subVisualizationId - the ID of the sub-visualization to switch to, such as - * lnsDatatable or bar_stacked + * lnsDatatable or bar */ async hasChartSwitchWarning(subVisualizationId: string, searchTerm?: string) { await this.openChartSwitchPopover(); @@ -1001,7 +1065,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont async createLayer( layerType: 'data' | 'referenceLine' | 'annotations' = 'data', annotationFromLibraryTitle?: string, - seriesType = 'bar_stacked' + seriesType = 'bar' ) { await testSubjects.click('lnsLayerAddButton'); const layerCount = await this.getLayerCount(); @@ -1698,7 +1762,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont }, hasEmptySizeRatioButtonGroup() { - return testSubjects.exists('lnsEmptySizeRatioButtonGroup'); + return testSubjects.exists('lnsEmptySizeRatioOption'); }, settingsMenuOpen() { diff --git a/x-pack/test/localization/tests/lens/smokescreen.ts b/x-pack/test/localization/tests/lens/smokescreen.ts index afd0822290f2c..82056c17df1ac 100644 --- a/x-pack/test/localization/tests/lens/smokescreen.ts +++ b/x-pack/test/localization/tests/lens/smokescreen.ts @@ -29,14 +29,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { return 'Tableau'; case 'bar': return 'Vertical à barres'; - case 'bar_stacked': - return 'Vertical à barres empilées'; - case 'bar_horizontal': - return 'Horizontal à barres'; case 'line': return 'Ligne'; - case 'donut': - return 'Graphique en anneau'; case 'pie': return 'Camembert'; case 'treemap': @@ -78,14 +72,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { return '表'; case 'bar': return '縦棒'; - case 'bar_stacked': - return '積み上げ縦棒'; - case 'bar_horizontal': - return '横棒'; case 'line': return '折れ線'; - case 'donut': - return 'ドーナッツ'; case 'pie': return '円'; case 'treemap': @@ -124,14 +112,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { return '表'; case 'bar': return '垂直条形图'; - case 'bar_stacked': - return '垂直堆积条形图'; - case 'bar_horizontal': - return '水平条形图'; case 'line': return '折线图'; - case 'donut': - return '圆环图'; case 'pie': return '饼图'; case 'treemap': @@ -210,7 +192,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.switchToVisualization('lnsDatatable', termTranslator('datatable')); await PageObjects.lens.removeDimension('lnsDatatable_rows'); - await PageObjects.lens.switchToVisualization('bar_stacked', termTranslator('bar_stacked')); + await PageObjects.lens.switchToVisualization('bar', termTranslator('bar')); await PageObjects.lens.configureDimension({ dimension: 'lnsXY_splitDimensionPanel > lns-empty-dimension', @@ -299,7 +281,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.switchToVisualization('line', termTranslator('line')); expect(await PageObjects.lens.getLayerType(0)).to.eql(termTranslator('line')); - expect(await PageObjects.lens.getLayerType(1)).to.eql(termTranslator('bar_stacked')); + expect(await PageObjects.lens.getLayerType(1)).to.eql(termTranslator('bar')); await PageObjects.lens.configureDimension({ dimension: 'lns-layerPanel-1 > lnsXY_xDimensionPanel > lns-empty-dimension', @@ -353,18 +335,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // only changes one layer for compatible chart await PageObjects.lens.switchToVisualization('line', termTranslator('line'), 1); - expect(await PageObjects.lens.getLayerType(0)).to.eql(termTranslator('bar_stacked')); + expect(await PageObjects.lens.getLayerType(0)).to.eql(termTranslator('bar')); expect(await PageObjects.lens.getLayerType(1)).to.eql(termTranslator('line')); - // changes all layers for multilayer chart - await PageObjects.lens.switchToVisualization( - 'bar_horizontal', - termTranslator('bar_horizontal'), - 1 - ); - expect(await PageObjects.lens.getLayerType(0)).to.eql(termTranslator('bar_horizontal')); - expect(await PageObjects.lens.getLayerType(1)).to.eql(termTranslator('bar_horizontal')); - // generates new one layer chart based on selected layer await PageObjects.lens.switchToVisualization('pie', termTranslator('pie'), 1); expect(await PageObjects.lens.getLayerType(0)).to.eql(termTranslator('pie')); @@ -485,7 +458,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should show value labels on bar charts when enabled', async () => { // enable value labels - await PageObjects.lens.openVisualOptions(); + await PageObjects.lens.openTextOptions(); await testSubjects.click('lns_valueLabels_inside'); // check for value labels @@ -493,7 +466,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(data?.bars?.[0].labels).not.to.eql(0); // switch to stacked bar chart - await PageObjects.lens.switchToVisualization('bar_stacked', termTranslator('bar_stacked')); + await PageObjects.lens.switchToVisualization('bar', termTranslator('bar')); // check for value labels data = await PageObjects.lens.getCurrentChartDebugState('xyVisChart'); @@ -517,15 +490,15 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(data?.axes?.y?.[1].gridlines.length).to.eql(0); }); - it('should transition from line chart to donut chart and to bar chart', async () => { + it('should transition from line chart to pie chart and to bar chart', async () => { await PageObjects.visualize.gotoVisualizationLandingPage(); await listingTable.searchForItemWithName('lnsXYvis'); await PageObjects.lens.clickVisualizeListItemTitle('lnsXYvis'); await PageObjects.lens.goToTimeRange(); - expect(await PageObjects.lens.hasChartSwitchWarning('donut', termTranslator('donut'))).to.eql( + expect(await PageObjects.lens.hasChartSwitchWarning('pie', termTranslator('pie'))).to.eql( true ); - await PageObjects.lens.switchToVisualization('donut', termTranslator('donut')); + await PageObjects.lens.switchToVisualization('pie', termTranslator('pie')); expect(await PageObjects.lens.getTitle()).to.eql('lnsXYvis'); expect(await PageObjects.lens.getDimensionTriggerText('lnsPie_sliceByDimensionPanel')).to.eql( @@ -540,7 +513,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ); await PageObjects.lens.switchToVisualization('bar', termTranslator('bar')); expect(await PageObjects.lens.getTitle()).to.eql('lnsXYvis'); - expect(await PageObjects.lens.getDimensionTriggerText('lnsXY_xDimensionPanel')).to.eql( + expect(await PageObjects.lens.getDimensionTriggerText('lnsXY_splitDimensionPanel')).to.eql( termTranslator('terms', 'ip') ); expect(await PageObjects.lens.getDimensionTriggerText('lnsXY_yDimensionPanel')).to.eql( @@ -949,10 +922,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await filterBar.removeFilter('extension.raw'); }); - it('should show visual options button group for a donut chart', async () => { + it('should show visual options button group for a pie chart', async () => { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickVisType('lens'); - await PageObjects.lens.switchToVisualization('donut', termTranslator('donut')); + await PageObjects.lens.switchToVisualization('pie', termTranslator('pie')); const hasVisualOptionsButton = await PageObjects.lens.hasVisualOptionsButton(); expect(hasVisualOptionsButton).to.be(true); @@ -962,14 +935,5 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(await PageObjects.lens.hasEmptySizeRatioButtonGroup()).to.be(true); }); }); - - it('should not show visual options button group for a pie chart', async () => { - await PageObjects.visualize.navigateToNewVisualization(); - await PageObjects.visualize.clickVisType('lens'); - await PageObjects.lens.switchToVisualization('pie', termTranslator('pie')); - - const hasVisualOptionsButton = await PageObjects.lens.hasVisualOptionsButton(); - expect(hasVisualOptionsButton).to.be(false); - }); }); } diff --git a/x-pack/test_serverless/functional/test_suites/common/visualizations/group1/smokescreen.ts b/x-pack/test_serverless/functional/test_suites/common/visualizations/group1/smokescreen.ts index 5dfeb08bbc5ad..1dbf90b831b4d 100644 --- a/x-pack/test_serverless/functional/test_suites/common/visualizations/group1/smokescreen.ts +++ b/x-pack/test_serverless/functional/test_suites/common/visualizations/group1/smokescreen.ts @@ -16,7 +16,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const testSubjects = getService('testSubjects'); const elasticChart = getService('elasticChart'); const filterBar = getService('filterBar'); - const retry = getService('retry'); const config = getService('config'); describe('lens smokescreen tests', () => { @@ -49,7 +48,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.switchToVisualization('lnsDatatable'); await PageObjects.lens.removeDimension('lnsDatatable_rows'); - await PageObjects.lens.switchToVisualization('bar_stacked'); + await PageObjects.lens.switchToVisualization('area'); await PageObjects.lens.configureDimension({ dimension: 'lnsXY_splitDimensionPanel > lns-empty-dimension', @@ -253,18 +252,11 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should show value labels on bar charts when enabled', async () => { // enable value labels - await PageObjects.lens.openVisualOptions(); + await PageObjects.lens.openTextOptions(); await testSubjects.click('lns_valueLabels_inside'); // check for value labels - let data = await PageObjects.lens.getCurrentChartDebugState('xyVisChart'); - expect(data?.bars?.[0].labels).not.to.eql(0); - - // switch to stacked bar chart - await PageObjects.lens.switchToVisualization('bar_stacked'); - - // check for value labels - data = await PageObjects.lens.getCurrentChartDebugState('xyVisChart'); + const data = await PageObjects.lens.getCurrentChartDebugState('xyVisChart'); expect(data?.bars?.[0].labels).not.to.eql(0); }); @@ -303,7 +295,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); await PageObjects.lens.createLayer('data', undefined, 'bar'); - expect(await PageObjects.lens.getLayerType(1)).to.eql('Bar vertical'); + expect(await PageObjects.lens.getLayerType(1)).to.eql('Bar'); await PageObjects.lens.configureDimension({ dimension: 'lns-layerPanel-1 > lnsXY_xDimensionPanel > lns-empty-dimension', @@ -329,13 +321,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ); }); - it('should transition from line chart to donut chart and to bar chart', async () => { + it('should transition from line chart to pie chart and to bar chart', async () => { await PageObjects.visualize.gotoVisualizationLandingPage(); await listingTable.searchForItemWithName('lnsXYvis'); await PageObjects.lens.clickVisualizeListItemTitle('lnsXYvis'); await PageObjects.lens.goToTimeRange(); - expect(await PageObjects.lens.hasChartSwitchWarning('donut')).to.eql(true); - await PageObjects.lens.switchToVisualization('donut'); + expect(await PageObjects.lens.hasChartSwitchWarning('pie')).to.eql(true); + await PageObjects.lens.switchToVisualization('pie'); expect(await PageObjects.lens.getTitle()).to.eql('lnsXYvis'); expect(await PageObjects.lens.getDimensionTriggerText('lnsPie_sliceByDimensionPanel')).to.eql( @@ -348,7 +340,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(await PageObjects.lens.hasChartSwitchWarning('bar')).to.eql(false); await PageObjects.lens.switchToVisualization('bar'); expect(await PageObjects.lens.getTitle()).to.eql('lnsXYvis'); - expect(await PageObjects.lens.getDimensionTriggerText('lnsXY_xDimensionPanel')).to.eql( + expect(await PageObjects.lens.getDimensionTriggerText('lnsXY_splitDimensionPanel')).to.eql( 'Top 3 values of ip' ); expect(await PageObjects.lens.getDimensionTriggerText('lnsXY_yDimensionPanel')).to.eql( @@ -744,27 +736,25 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await filterBar.removeFilter('extension.raw'); }); - it('should show visual options button group for a donut chart', async () => { + it('should show visual options button group for a pie chart', async () => { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickVisType('lens'); - await PageObjects.lens.switchToVisualization('donut'); + await PageObjects.lens.switchToVisualization('pie'); const hasVisualOptionsButton = await PageObjects.lens.hasVisualOptionsButton(); expect(hasVisualOptionsButton).to.be(true); - await PageObjects.lens.openVisualOptions(); - await retry.try(async () => { - expect(await PageObjects.lens.hasEmptySizeRatioButtonGroup()).to.be(true); - }); + const donutHole = await PageObjects.lens.getDonutHoleSize(); + expect(donutHole).to.be('None'); }); - it('should not show visual options button group for a pie chart', async () => { + it('switches donut hole size', async () => { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickVisType('lens'); await PageObjects.lens.switchToVisualization('pie'); - - const hasVisualOptionsButton = await PageObjects.lens.hasVisualOptionsButton(); - expect(hasVisualOptionsButton).to.be(false); + await PageObjects.lens.setDonutHoleSize('Small'); + const donutHole = await PageObjects.lens.getDonutHoleSize(); + expect(donutHole).to.be('Small'); }); }); } diff --git a/x-pack/test_serverless/functional/test_suites/common/visualizations/group2/open_in_lens/agg_based/pie.ts b/x-pack/test_serverless/functional/test_suites/common/visualizations/group2/open_in_lens/agg_based/pie.ts index 248bd25ae7f90..5ede82b1d0b43 100644 --- a/x-pack/test_serverless/functional/test_suites/common/visualizations/group2/open_in_lens/agg_based/pie.ts +++ b/x-pack/test_serverless/functional/test_suites/common/visualizations/group2/open_in_lens/agg_based/pie.ts @@ -85,13 +85,15 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await pieChart.expectPieChartLabels(expectedTableData); }); - it('should convert Donut type correctly', async () => { + it('should convert pie with hole type correctly', async () => { await panelActions.convertToLensByTitle('Pie - Basic count'); await lens.waitForVisualization('partitionVisChart'); const chartSwitcher = await testSubjects.find('lnsChartSwitchPopover'); const type = await chartSwitcher.getVisibleText(); - expect(type).to.be('Donut'); + expect(type).to.be('Pie'); + const donutHole = await lens.getDonutHoleSize(); + expect(donutHole).to.be('Small'); }); it('should convert Pie types correctly', async () => { diff --git a/x-pack/test_serverless/functional/test_suites/common/visualizations/group2/open_in_lens/agg_based/xy.ts b/x-pack/test_serverless/functional/test_suites/common/visualizations/group2/open_in_lens/agg_based/xy.ts index 1ec4c040ab916..53cf01133e1ff 100644 --- a/x-pack/test_serverless/functional/test_suites/common/visualizations/group2/open_in_lens/agg_based/xy.ts +++ b/x-pack/test_serverless/functional/test_suites/common/visualizations/group2/open_in_lens/agg_based/xy.ts @@ -77,7 +77,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const layerChartSwitches = await testSubjects.findAll('lnsChartSwitchPopover'); expect(layerChartSwitches.length).to.be(2); expect(await layerChartSwitches[0].getVisibleText()).to.be('Area'); - expect(await layerChartSwitches[1].getVisibleText()).to.be('Bar vertical'); + expect(await layerChartSwitches[1].getVisibleText()).to.be('Bar'); const yDimensionText1 = await lens.getDimensionTriggerText('lnsXY_yDimensionPanel', 0); const yDimensionText2 = await lens.getDimensionTriggerText('lnsXY_yDimensionPanel', 1); expect(yDimensionText1).to.be('Count'); @@ -93,7 +93,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { expect(await lens.getLayerCount()).to.be(1); const layerChartSwitches = await testSubjects.findAll('lnsChartSwitchPopover'); expect(layerChartSwitches.length).to.be(1); - expect(await layerChartSwitches[0].getVisibleText()).to.be('Bar vertical'); + expect(await layerChartSwitches[0].getVisibleText()).to.be('Bar'); const yDimensionText1 = await lens.getDimensionTriggerText('lnsXY_yDimensionPanel', 0); const yDimensionText2 = await lens.getDimensionTriggerText('lnsXY_yDimensionPanel', 1); expect(yDimensionText1).to.be('Count'); @@ -156,7 +156,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { expect(await lens.getLayerCount()).to.be(1); const layerChartSwitches = await testSubjects.findAll('lnsChartSwitchPopover'); expect(layerChartSwitches.length).to.be(1); - expect(await layerChartSwitches[0].getVisibleText()).to.be('Area stacked'); + expect(await layerChartSwitches[0].getVisibleText()).to.be('Area'); }); }); @@ -168,7 +168,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { expect(await lens.getLayerCount()).to.be(1); const layerChartSwitches = await testSubjects.findAll('lnsChartSwitchPopover'); expect(layerChartSwitches.length).to.be(1); - expect(await layerChartSwitches[0].getVisibleText()).to.be('Area percentage'); + expect(await layerChartSwitches[0].getVisibleText()).to.be('Area'); }); }); @@ -180,7 +180,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { expect(await lens.getLayerCount()).to.be(1); const layerChartSwitches = await testSubjects.findAll('lnsChartSwitchPopover'); expect(layerChartSwitches.length).to.be(1); - expect(await layerChartSwitches[0].getVisibleText()).to.be('Bar horizontal'); + expect(await layerChartSwitches[0].getVisibleText()).to.be('Bar'); + expect(await lens.getSelectedBarOrientationSetting()).to.be('Horizontal'); }); }); diff --git a/x-pack/test_serverless/functional/test_suites/common/visualizations/group3/open_in_lens/tsvb/top_n.ts b/x-pack/test_serverless/functional/test_suites/common/visualizations/group3/open_in_lens/tsvb/top_n.ts index e0286c78bfb33..85369142a3fec 100644 --- a/x-pack/test_serverless/functional/test_suites/common/visualizations/group3/open_in_lens/tsvb/top_n.ts +++ b/x-pack/test_serverless/functional/test_suites/common/visualizations/group3/open_in_lens/tsvb/top_n.ts @@ -66,7 +66,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const chartSwitcher = await testSubjects.find('lnsChartSwitchPopover'); const type = await chartSwitcher.getVisibleText(); - expect(type).to.be('Bar horizontal'); + expect(type).to.be('Bar'); await retry.try(async () => { const layerCount = await lens.getLayerCount(); expect(layerCount).to.be(1); diff --git a/x-pack/test_serverless/functional/test_suites/search/dashboards/build_dashboard.ts b/x-pack/test_serverless/functional/test_suites/search/dashboards/build_dashboard.ts index 06d2d111da797..1f13f10d9e948 100644 --- a/x-pack/test_serverless/functional/test_suites/search/dashboards/build_dashboard.ts +++ b/x-pack/test_serverless/functional/test_suites/search/dashboards/build_dashboard.ts @@ -58,7 +58,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await PageObjects.dashboard.waitForRenderComplete(); await dashboardPanelActions.openContextMenu(); await dashboardPanelActions.clickEdit(); - await PageObjects.lens.switchToVisualization('donut'); + await PageObjects.lens.switchToVisualization('pie'); await PageObjects.lens.saveAndReturn(); await PageObjects.dashboard.waitForRenderComplete();