diff --git a/x-pack/platform/plugins/private/translations/translations/de-DE.json b/x-pack/platform/plugins/private/translations/translations/de-DE.json index fef7917c7797a..38c906c7a5faa 100644 --- a/x-pack/platform/plugins/private/translations/translations/de-DE.json +++ b/x-pack/platform/plugins/private/translations/translations/de-DE.json @@ -23295,8 +23295,6 @@ "xpack.lens.messagesButton.label.warnings": "{warningCount} {warningCount, plural, one {warning} other {warnings}}", "xpack.lens.metric.addLayer": "Visualisierung", "xpack.lens.metric.breakdownBy": "Aufschlüsselung nach", - "xpack.lens.metric.color": "Farbe", - "xpack.lens.metric.colorByValue.label": "Farbe nach Nutzen", "xpack.lens.metric.colorMode.dynamic": "Dynamisch", "xpack.lens.metric.colorMode.static": "Statisch", "xpack.lens.metric.headingLabel": "Wert", diff --git a/x-pack/platform/plugins/private/translations/translations/fr-FR.json b/x-pack/platform/plugins/private/translations/translations/fr-FR.json index 66b55d834e5f2..c9595fe6fd374 100644 --- a/x-pack/platform/plugins/private/translations/translations/fr-FR.json +++ b/x-pack/platform/plugins/private/translations/translations/fr-FR.json @@ -23607,8 +23607,6 @@ "xpack.lens.messagesButton.label.warnings": "{warningCount} {warningCount, plural, one {warning} other {avertissements}}", "xpack.lens.metric.addLayer": "Visualisation", "xpack.lens.metric.breakdownBy": "Répartir par", - "xpack.lens.metric.color": "Couleur", - "xpack.lens.metric.colorByValue.label": "Couleur par valeur", "xpack.lens.metric.colorMode.dynamic": "Dynamique", "xpack.lens.metric.colorMode.static": "Statique", "xpack.lens.metric.headingLabel": "Valeur", diff --git a/x-pack/platform/plugins/private/translations/translations/ja-JP.json b/x-pack/platform/plugins/private/translations/translations/ja-JP.json index e72094b8f23b5..4edffce5491ba 100644 --- a/x-pack/platform/plugins/private/translations/translations/ja-JP.json +++ b/x-pack/platform/plugins/private/translations/translations/ja-JP.json @@ -23636,8 +23636,6 @@ "xpack.lens.messagesButton.label.warnings": "{warningCount}{warningCount, plural, one {warning} other {件の警告}}", "xpack.lens.metric.addLayer": "ビジュアライゼーション", "xpack.lens.metric.breakdownBy": "分類する", - "xpack.lens.metric.color": "色", - "xpack.lens.metric.colorByValue.label": "値別の色", "xpack.lens.metric.colorMode.dynamic": "動的", "xpack.lens.metric.colorMode.static": "静的", "xpack.lens.metric.headingLabel": "値", diff --git a/x-pack/platform/plugins/private/translations/translations/zh-CN.json b/x-pack/platform/plugins/private/translations/translations/zh-CN.json index a1fca4b831a46..89037a5c99f22 100644 --- a/x-pack/platform/plugins/private/translations/translations/zh-CN.json +++ b/x-pack/platform/plugins/private/translations/translations/zh-CN.json @@ -23624,8 +23624,6 @@ "xpack.lens.messagesButton.label.warnings": "{warningCount} 个{warningCount, plural, one {警告} other {警告}}", "xpack.lens.metric.addLayer": "可视化", "xpack.lens.metric.breakdownBy": "细分方式", - "xpack.lens.metric.color": "颜色", - "xpack.lens.metric.colorByValue.label": "按值上色", "xpack.lens.metric.colorMode.dynamic": "动态", "xpack.lens.metric.colorMode.static": "静态", "xpack.lens.metric.headingLabel": "值", diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/metric/dimension_editor.test.tsx b/x-pack/platform/plugins/shared/lens/public/visualizations/metric/dimension_editor.test.tsx index d896543e8f171..19c71dd750d99 100644 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/metric/dimension_editor.test.tsx +++ b/x-pack/platform/plugins/shared/lens/public/visualizations/metric/dimension_editor.test.tsx @@ -158,23 +158,7 @@ describe('dimension editor', () => { /> ); - const colorModeGroup = screen.queryByRole('group', { name: /Color by value/i }); - const staticColorPicker = screen.queryByTestId(SELECTORS.COLOR_PICKER); - - const typeColor = async (color: string) => { - if (!staticColorPicker) { - throw new Error('Static color picker not found'); - } - await userEvent.clear(staticColorPicker); - await userEvent.type(staticColorPicker, color); - }; - - const clearColor = async () => { - if (!staticColorPicker) { - throw new Error('Static color picker not found'); - } - await userEvent.clear(staticColorPicker); - }; + const getStaticColorPicker = () => screen.queryByTestId(SELECTORS.COLOR_PICKER); const iconSelect = screen.getByTestId('lns-icon-select'); const setIcon = async (icon: string) => { @@ -209,10 +193,7 @@ describe('dimension editor', () => { }; return { - colorModeGroup, - staticColorPicker, - typeColor, - clearColor, + getStaticColorPicker, setIcon, clearIcon, ...rtlRender, @@ -227,90 +208,12 @@ describe('dimension editor', () => { expect(screen.queryByTestId(SELECTORS.BREAKDOWN_EDITOR)).not.toBeInTheDocument(); }); - it('Color mode switch is shown when the primary metric is numeric', () => { - const { colorModeGroup } = renderPrimaryMetricEditor(); - expect(colorModeGroup).toBeInTheDocument(); - }); - - it('Color mode switch is not shown when the primary metric is non-numeric', () => { - const { colorModeGroup } = renderPrimaryMetricEditor({ + it('static color control is visible when metric is non-numeric even if palette is set', () => { + const { getStaticColorPicker } = renderPrimaryMetricEditor({ datasource: getNonNumericDatasource(), + state: { ...metricAccessorState, palette }, }); - expect(colorModeGroup).not.toBeInTheDocument(); - }); - - it('Color mode switch is not shown when the primary metric is numeric but with array support', () => { - const { colorModeGroup } = renderPrimaryMetricEditor({ - datasource: getNumericDatasourceWithArraySupport(), - }); - expect(colorModeGroup).not.toBeInTheDocument(); - }); - - describe('static color controls', () => { - it('is hidden when dynamic coloring is enabled', () => { - const { staticColorPicker } = renderPrimaryMetricEditor({ - state: { ...metricAccessorState, palette }, - }); - expect(staticColorPicker).not.toBeInTheDocument(); - }); - - it('is visible if palette is not defined', () => { - const { staticColorPicker } = renderPrimaryMetricEditor({ - state: { ...metricAccessorState, palette: undefined }, - }); - expect(staticColorPicker).toBeInTheDocument(); - }); - - it('is visible when metric is non-numeric even if palette is set', () => { - const { staticColorPicker } = renderPrimaryMetricEditor({ - datasource: getNonNumericDatasource(), - state: { ...metricAccessorState, palette }, - }); - expect(staticColorPicker).toBeInTheDocument(); - }); - - it('fills with default EUI visualization color value', () => { - const { staticColorPicker } = renderPrimaryMetricEditor({ - state: { - ...metricAccessorState, - palette: undefined, - color: undefined, - }, - }); - expect(staticColorPicker).toHaveValue(euiLightVars.euiColorPrimary.toUpperCase()); - }); - - it('fills with default vis text color', async () => { - const { staticColorPicker } = renderPrimaryMetricEditor({ - state: { - ...metricAccessorState, - palette: undefined, // color by value static - trendlineLayerId: undefined, - showBar: false, - color: undefined, - applyColorTo: 'value', - }, - }); - expect(staticColorPicker).toHaveValue(euiThemeVars.euiColorVisText0.toUpperCase()); - }); - - it('sets color', async () => { - const { typeColor, clearColor } = renderPrimaryMetricEditor({ - state: { ...metricAccessorState, palette: undefined, color: faker.color.rgb() }, - }); - - const newColor = faker.color.rgb().toUpperCase(); - await typeColor(newColor); - await waitFor(() => - expect(mockSetState).toHaveBeenCalledWith(expect.objectContaining({ color: newColor })) - ); - await clearColor(); - await waitFor(() => - expect(mockSetState).toHaveBeenCalledWith(expect.objectContaining({ color: undefined })) - ); - - expect(mockSetState).toHaveBeenCalledTimes(2); - }); + expect(getStaticColorPicker()).toBeInTheDocument(); }); describe('icon select', () => { @@ -423,11 +326,11 @@ describe('dimension editor', () => { const getSelectedPalette = (name: string) => screen.queryByRole('button', { name: new RegExp(name, 'i') }); - const colorModeGroup = screen.getByRole('group', { name: /Color by value/i }); + const colorModeGroup = screen.getByRole('group', { name: /Color mode/i }); const clickOnColorMode = async (mode: 'none' | 'static' | 'dynamic') => { const colorByValueOption = getByTitle(colorModeGroup, mode, { exact: false }); if (!colorByValueOption) { - throw new Error(`Supported color by value ${mode} not found`); + throw new Error(`Supported color mode ${mode} not found`); } await userEvent.click(colorByValueOption); }; @@ -1062,6 +965,24 @@ describe('dimension editor', () => { await userEvent.click(applyColorTo); }; + const colorModeGroup = screen.queryByRole('group', { name: /Color mode/i }); + const staticColorPicker = screen.queryByTestId(SELECTORS.COLOR_PICKER); + + const typeColor = async (color: string) => { + if (!staticColorPicker) { + throw new Error('Static color picker not found'); + } + await userEvent.clear(staticColorPicker); + await userEvent.type(staticColorPicker, color); + }; + + const clearColor = async () => { + if (!staticColorPicker) { + throw new Error('Static color picker not found'); + } + await userEvent.clear(staticColorPicker); + }; + return { progressDirectionShowing: screen.queryByTestId('lnsMetric_progress_direction_buttons'), progressOptions: { @@ -1073,6 +994,10 @@ describe('dimension editor', () => { applyColorToBtnGroup: screen.queryByTestId('lnsMetric_apply_color_to_buttons'), applyColorToOptions, clickOnApplyColorToOption, + colorModeGroup, + staticColorPicker, + typeColor, + clearColor, ...rtlRender, }; } @@ -1305,7 +1230,7 @@ describe('dimension editor', () => { expect(mockSetState).toHaveBeenCalledWith({ ...mockState, applyColorTo: 'background' }); }); - it('should show help message when color by value static, supporting visualization is panel, apply color to value', () => { + it('should show help message when color mode static, supporting visualization is panel, apply color to value', () => { renderAdditionalSectionEditor({ state: { ...stateWOTrend, @@ -1321,7 +1246,7 @@ describe('dimension editor', () => { ); }); - it('should show help message when color by value dynamic, supporting visualization is panel, apply color to value', () => { + it('should show help message when color mode dynamic, supporting visualization is panel, apply color to value', () => { renderAdditionalSectionEditor({ state: { ...stateWOTrend, @@ -1333,5 +1258,83 @@ describe('dimension editor', () => { }); }); }); + + it('Color mode switch is shown when the primary metric is numeric', () => { + const { colorModeGroup } = renderAdditionalSectionEditor(); + expect(colorModeGroup).toBeInTheDocument(); + }); + + it('Color mode switch is not shown when the primary metric is non-numeric', () => { + const { colorModeGroup } = renderAdditionalSectionEditor({ + datasource: getNonNumericDatasource(), + }); + expect(colorModeGroup).not.toBeInTheDocument(); + }); + + it('Color mode switch is not shown when the primary metric is numeric but with array support', () => { + const { colorModeGroup } = renderAdditionalSectionEditor({ + datasource: getNumericDatasourceWithArraySupport(), + }); + expect(colorModeGroup).not.toBeInTheDocument(); + }); + + describe('static color controls', () => { + it('is hidden when dynamic coloring is enabled', () => { + const { staticColorPicker } = renderAdditionalSectionEditor({ + state: { ...metricAccessorState, palette }, + }); + expect(staticColorPicker).not.toBeInTheDocument(); + }); + + it('is visible if palette is not defined', () => { + const { staticColorPicker } = renderAdditionalSectionEditor({ + state: { ...metricAccessorState, palette: undefined }, + }); + expect(staticColorPicker).toBeInTheDocument(); + }); + + it('fills with default EUI visualization color value', () => { + const { staticColorPicker } = renderAdditionalSectionEditor({ + state: { + ...metricAccessorState, + palette: undefined, + color: undefined, + }, + }); + expect(staticColorPicker).toHaveValue(euiLightVars.euiColorPrimary.toUpperCase()); + }); + + it('fills with default vis text color', async () => { + const { staticColorPicker } = renderAdditionalSectionEditor({ + state: { + ...metricAccessorState, + palette: undefined, + trendlineLayerId: undefined, + showBar: false, + color: undefined, + applyColorTo: 'value', + }, + }); + expect(staticColorPicker).toHaveValue(euiThemeVars.euiColorVisText0.toUpperCase()); + }); + + it('sets color', async () => { + const { typeColor, clearColor } = renderAdditionalSectionEditor({ + state: { ...metricAccessorState, palette: undefined, color: faker.color.rgb() }, + }); + + const newColor = faker.color.rgb().toUpperCase(); + await typeColor(newColor); + await waitFor(() => + expect(mockSetState).toHaveBeenCalledWith(expect.objectContaining({ color: newColor })) + ); + await clearColor(); + await waitFor(() => + expect(mockSetState).toHaveBeenCalledWith(expect.objectContaining({ color: undefined })) + ); + + expect(mockSetState).toHaveBeenCalledTimes(2); + }); + }); }); }); diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/metric/dimension_editor.tsx b/x-pack/platform/plugins/shared/lens/public/visualizations/metric/dimension_editor.tsx index a62b0027e479a..863ed16c0c50e 100644 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/metric/dimension_editor.tsx +++ b/x-pack/platform/plugins/shared/lens/public/visualizations/metric/dimension_editor.tsx @@ -56,9 +56,11 @@ export type Props = VisualizationDimensionEditorProps paletteService: PaletteRegistry; }; -type SubProps = Props & { idPrefix: string }; +type SubProps = VisualizationDimensionEditorProps & { idPrefix: string }; -export function DimensionEditor(props: Props) { +export function DimensionEditor( + props: VisualizationDimensionEditorProps +) { const { state, accessor } = props; const idPrefix = htmlIdGenerator()(); @@ -577,15 +579,15 @@ function SecondaryMetricEditor({ state.trendlineLayerId ? 'trendline' : showingBar(state) ? 'bar' : 'panel'; -function PrimaryMetricEditor(props: SubProps) { - const { state, setState, frame, accessor, idPrefix, isInlineEditing } = props; - const { isNumeric: isMetricNumeric } = getAccessorType(props.datasource, accessor); - - const euiTheme = useEuiTheme(); - +function PrimaryMetricEditor({ state, setState, datasource, accessor }: SubProps) { + const { isNumeric: isMetricNumeric } = getAccessorType(datasource, accessor); const setColor = useCallback( (color: string) => { setState({ ...state, color: color === '' ? undefined : color }); }, [setState, state] ); - const getColor = useCallback(() => { return state.color || getDefaultColor(state, isMetricNumeric); }, [state, isMetricNumeric]); @@ -681,138 +678,15 @@ function PrimaryMetricEditor(props: SubProps) { return null; } - const hasDynamicColoring = Boolean(isMetricNumeric && state.palette); - - const supportsPercentPalette = Boolean( - state.maxAccessor || - (state.breakdownByAccessor && !state.collapseFn) || - state.palette?.params?.rangeType === 'percent' - ); - - const activePalette = state.palette || { - type: 'palette', - name: (supportsPercentPalette ? defaultPercentagePaletteParams : defaultNumberPaletteParams) - .name, - params: { - ...(supportsPercentPalette ? defaultPercentagePaletteParams : defaultNumberPaletteParams), - }, - }; - - const currentMinMax = getDataBoundsForPalette( - { - metric: state.metricAccessor!, - max: state.maxAccessor, - // if we're collapsing, pretend like there's no breakdown to match the activeData - breakdownBy: !state.collapseFn ? state.breakdownByAccessor : undefined, - }, - frame.activeData?.[state.layerId] - ); - - const displayStops = applyPaletteParams(props.paletteService, activePalette, { - min: currentMinMax.min ?? DEFAULT_MIN_STOP, - max: currentMinMax.max ?? DEFAULT_MAX_STOP, - }); - - const showVisTextColorSwatches = - supportingVisualization(state) === 'panel' && state.applyColorTo === 'value'; - - const colorByValue = state.palette ? 'dynamic' : 'static'; + // Show static color control in Primary Metric editor when is not numeric. + // Non-numeric metrics do not support the "Supporting visualization" section + const showStaticColorControl = !isMetricNumeric; return (
- {isMetricNumeric && ( - - { - if (newColorByValue === colorByValue) return; - - setState({ - ...state, - ...(newColorByValue === 'dynamic' - ? { - palette: { - ...activePalette, - params: { - ...activePalette.params, - stops: displayStops, - }, - }, - color: undefined, - } - : { - palette: undefined, - color: undefined, - }), - }); - }} - /> - - )} - {hasDynamicColoring ? ( - - color)} - siblingRef={props.panelRef} - isInlineEditing={isInlineEditing} - > - { - setState({ - ...state, - palette: newPalette, - }); - }} - /> - - - ) : ( - - )} + {showStaticColorControl ? ( + + ) : null} void; swatches?: string[]; }) { - const colorLabel = i18n.translate('xpack.lens.metric.color', { - defaultMessage: 'Color', + const colorLabel = i18n.translate('xpack.lens.metric.selectColor.label', { + defaultMessage: 'Select color', }); const { inputValue: currentColor, handleInputChange: handleColorChange } = @@ -921,6 +795,12 @@ function StaticColorControl({ ); } +/** + * Supporting Visualization Section: + * + * This section allows users to configure the "Supporting Visualization" for the metric visualization. + * Users can choose between different visualization types (Panel, Bar, or Trendline) based on the data and configuration. + */ export function DimensionEditorAdditionalSection({ state, datasource, @@ -929,10 +809,30 @@ export function DimensionEditorAdditionalSection({ removeLayer, accessor, frame, -}: VisualizationDimensionEditorProps) { - const { euiTheme } = useEuiTheme(); + paletteService, + panelRef, + isInlineEditing, +}: Props) { + const euiThemeContext = useEuiTheme(); + const { euiTheme } = euiThemeContext; const { isNumeric: isMetricNumeric } = getAccessorType(datasource, accessor); + + const setColor = useCallback( + (color: string) => { + setState({ ...state, color: color === '' ? undefined : color }); + }, + [setState, state] + ); + + const getColor = useCallback(() => { + return state.color || getDefaultColor(state, isMetricNumeric); + }, [state, isMetricNumeric]); + + if (accessor == null) { + return null; + } + if (accessor !== state.metricAccessor || !isMetricNumeric) { return null; } @@ -987,6 +887,43 @@ export function DimensionEditorAdditionalSection({ const selectedSupportingVisualization = supportingVisualization(state); + const hasDynamicColoring = Boolean(isMetricNumeric && state.palette); + + const supportsPercentPalette = Boolean( + state.maxAccessor || + (state.breakdownByAccessor && !state.collapseFn) || + state.palette?.params?.rangeType === 'percent' + ); + + const activePalette = state.palette || { + type: 'palette', + name: (supportsPercentPalette ? defaultPercentagePaletteParams : defaultNumberPaletteParams) + .name, + params: { + ...(supportsPercentPalette ? defaultPercentagePaletteParams : defaultNumberPaletteParams), + }, + }; + + const currentMinMax = getDataBoundsForPalette( + { + metric: state.metricAccessor!, + max: state.maxAccessor, + // if we're collapsing, pretend like there's no breakdown to match the activeData + breakdownBy: !state.collapseFn ? state.breakdownByAccessor : undefined, + }, + frame.activeData?.[state.layerId] + ); + + const displayStops = applyPaletteParams(paletteService, activePalette, { + min: currentMinMax.min ?? DEFAULT_MIN_STOP, + max: currentMinMax.max ?? DEFAULT_MAX_STOP, + }); + + const showVisTextColorSwatches = + supportingVisualization(state) === 'panel' && state.applyColorTo === 'value'; + + const colorMode = state.palette ? 'dynamic' : 'static'; + return (
)} + + {isMetricNumeric && ( + + { + if (newColorMode === colorMode) return; + + setState({ + ...state, + ...(newColorMode === 'dynamic' + ? { + palette: { + ...activePalette, + params: { + ...activePalette.params, + stops: displayStops, + }, + }, + color: undefined, + } + : { + palette: undefined, + color: undefined, + }), + }); + }} + /> + + )} + {hasDynamicColoring ? ( + + color)} + siblingRef={panelRef} + isInlineEditing={isInlineEditing} + > + { + setState({ + ...state, + palette: newPalette, + }); + }} + /> + + + ) : ( + + )}
); diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/metric/visualization.tsx b/x-pack/platform/plugins/shared/lens/public/visualizations/metric/visualization.tsx index 89e862d78be0d..13bf4f23687a0 100644 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/metric/visualization.tsx +++ b/x-pack/platform/plugins/shared/lens/public/visualizations/metric/visualization.tsx @@ -673,11 +673,11 @@ export const getMetricVisualization = ({ }, DimensionEditorComponent(props) { - return ; + return ; }, DimensionEditorAdditionalSectionComponent(props) { - return ; + return ; }, getDisplayOptions() {