diff --git a/packages/kbn-coloring/BUILD.bazel b/packages/kbn-coloring/BUILD.bazel index 656807d8ba2c1..cbbb4d2b7d3e8 100644 --- a/packages/kbn-coloring/BUILD.bazel +++ b/packages/kbn-coloring/BUILD.bazel @@ -49,6 +49,8 @@ RUNTIME_DEPS = [ "@npm//@elastic/eui", "@npm//react-use", "@npm//react", + "@npm//@emotion/react", + "@npm//@emotion/css", ] # In this array place dependencies necessary to build the types, which will include the @@ -71,6 +73,8 @@ TYPES_DEPS = [ "@npm//@types/react", "@npm//@elastic/eui", "@npm//react-use", + "@npm//@emotion/react", + "@npm//@emotion/css", ] jsts_transpiler( diff --git a/packages/kbn-coloring/src/shared_components/coloring/palette_configuration.scss b/packages/kbn-coloring/src/shared_components/coloring/palette_configuration.scss deleted file mode 100644 index 82165b172eab9..0000000000000 --- a/packages/kbn-coloring/src/shared_components/coloring/palette_configuration.scss +++ /dev/null @@ -1,8 +0,0 @@ -.lnsPalettePanel__section { - padding: $euiSize; -} - -.lnsPalettePanel__section--shaded { - background-color: $euiColorLightestShade; - border-bottom: $euiBorderThin; -} \ No newline at end of file diff --git a/packages/kbn-coloring/src/shared_components/coloring/palette_configuration.tsx b/packages/kbn-coloring/src/shared_components/coloring/palette_configuration.tsx index beb7e828c1435..5db80ba80446c 100644 --- a/packages/kbn-coloring/src/shared_components/coloring/palette_configuration.tsx +++ b/packages/kbn-coloring/src/shared_components/coloring/palette_configuration.tsx @@ -8,7 +8,8 @@ import { i18n } from '@kbn/i18n'; import React, { useReducer, useMemo } from 'react'; import useDebounce from 'react-use/lib/useDebounce'; -import { EuiFormRow, htmlIdGenerator, EuiButtonGroup, EuiIconTip } from '@elastic/eui'; +import { css } from '@emotion/react'; +import { EuiFormRow, htmlIdGenerator, EuiButtonGroup, EuiIconTip, useEuiTheme } from '@elastic/eui'; import { PalettePicker } from './palette_picker'; import { PaletteOutput, @@ -19,8 +20,6 @@ import { RequiredPaletteParamTypes, } from '../../palettes'; -import './palette_configuration.scss'; - import { toColorRanges } from './utils'; import { ColorRanges, ColorRangesContext } from './color_ranges'; import { isAllColorRangesValid } from './color_ranges/color_ranges_validation'; @@ -72,8 +71,19 @@ export const CustomizablePalette = ({ [localState] ); + const { euiTheme } = useEuiTheme(); + + const styles = useMemo( + () => css` + padding: ${euiTheme.size.base}; + background-color: ${euiTheme.colors.lightestShade}; + border-bottom: ${euiTheme.border.thin}; + `, + [euiTheme.size.base, euiTheme.colors.lightestShade, euiTheme.border.thin] + ); + return ( -
+
) => ( - - {title ? {title} : null} - - - -); +export const HorizontalBulletIcon = ({ title, titleId, ...props }: Omit) => { + const { chartIcon } = useCommonChartStyles(); + + return ( + + {title ? {title} : null} + + + + ); +}; diff --git a/src/plugins/chart_expressions/expression_gauge/public/components/icons/vertical_bullet_icon.tsx b/src/plugins/chart_expressions/expression_gauge/public/components/icons/vertical_bullet_icon.tsx index 99f4976d5f38f..49a1e2a817cf7 100644 --- a/src/plugins/chart_expressions/expression_gauge/public/components/icons/vertical_bullet_icon.tsx +++ b/src/plugins/chart_expressions/expression_gauge/public/components/icons/vertical_bullet_icon.tsx @@ -8,25 +8,30 @@ import React from 'react'; import type { EuiIconProps } from '@elastic/eui'; +import { useCommonChartStyles } from '@kbn/charts-plugin/public'; -export const VerticalBulletIcon = ({ title, titleId, ...props }: Omit) => ( - - {title ? {title} : null} - - - -); +export const VerticalBulletIcon = ({ title, titleId, ...props }: Omit) => { + const { chartIcon } = useCommonChartStyles(); + + return ( + + {title ? {title} : null} + + + + ); +}; diff --git a/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.test.tsx b/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.test.tsx index 6b073b07d4841..4f3e77b8f1d6e 100644 --- a/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.test.tsx +++ b/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.test.tsx @@ -27,6 +27,17 @@ jest.mock('@elastic/charts', () => { }; }); +const actWithTimeout = (action: Function, timer: number = 1) => + act( + () => + new Promise((resolve) => + setTimeout(async () => { + await action(); + resolve(); + }, timer) + ) + ); + const chartsThemeService = chartPluginMock.createSetupContract().theme; const palettesRegistry = chartPluginMock.createPaletteRegistry(); const formatService = fieldFormatsServiceMock.createStartContract(); @@ -110,6 +121,9 @@ describe('HeatmapComponent', function () { it('renders the legend toggle component if uiState is set', async () => { const component = mountWithIntl(); + await actWithTimeout(async () => { + await component.update(); + }); await act(async () => { expect(findTestSubject(component, 'vislibToggleLegend').length).toBe(1); }); @@ -118,6 +132,9 @@ describe('HeatmapComponent', function () { it('not renders the legend toggle component if uiState is undefined', async () => { const newProps = { ...wrapperProps, uiState: undefined } as unknown as HeatmapRenderProps; const component = mountWithIntl(); + await actWithTimeout(async () => { + await component.update(); + }); await act(async () => { expect(findTestSubject(component, 'vislibToggleLegend').length).toBe(0); }); @@ -125,6 +142,9 @@ describe('HeatmapComponent', function () { it('renders the legendColorPicker if uiState is set', async () => { const component = mountWithIntl(); + await actWithTimeout(async () => { + await component.update(); + }); await act(async () => { expect(component.find(Settings).prop('legendColorPicker')).toBeDefined(); }); @@ -133,6 +153,9 @@ describe('HeatmapComponent', function () { it('not renders the legendColorPicker if uiState is undefined', async () => { const newProps = { ...wrapperProps, uiState: undefined } as unknown as HeatmapRenderProps; const component = mountWithIntl(); + await actWithTimeout(async () => { + await component.update(); + }); await act(async () => { expect(component.find(Settings).prop('legendColorPicker')).toBeUndefined(); }); diff --git a/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_icon.tsx b/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_icon.tsx index 410f8a268bff7..7a49d53222946 100644 --- a/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_icon.tsx +++ b/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_icon.tsx @@ -8,25 +8,30 @@ import { EuiIconProps } from '@elastic/eui'; import React from 'react'; +import { useCommonChartStyles } from '@kbn/charts-plugin/public'; -export const HeatmapIcon = ({ title, titleId, ...props }: Omit) => ( - - {title ? {title} : null} - - - -); +export const HeatmapIcon = ({ title, titleId, ...props }: Omit) => { + const { chartIcon } = useCommonChartStyles(); + + return ( + + {title ? {title} : null} + + + + ); +}; diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap b/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap index 2df18b5813473..3c48d3cb36771 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap @@ -228,7 +228,7 @@ exports[`PartitionVisComponent should render correct structure for donut 1`] = ` } } > - - - - - { }; }); +const actWithTimeout = (action: Function, timer: number = 1) => + act( + () => + new Promise((resolve) => + setTimeout(async () => { + await action(); + resolve(); + }, timer) + ) + ); + const chartsThemeService = chartPluginMock.createSetupContract().theme; const palettesRegistry = chartPluginMock.createPaletteRegistry(); const visParams = createMockPieParams(); @@ -131,13 +142,12 @@ describe('PartitionVisComponent', function () { expect(component).toMatchSnapshot(); }); - it('renders the legend on the correct position', () => { - const component = shallow(); - expect(component.find(Settings).prop('legendPosition')).toEqual('right'); - }); - it('renders the legend toggle component', async () => { const component = mount(); + await actWithTimeout(async () => { + await component.update(); + }); + await act(async () => { expect(findTestSubject(component, 'vislibToggleLegend').length).toBe(1); }); @@ -145,6 +155,9 @@ describe('PartitionVisComponent', function () { it('hides the legend if the legend toggle is clicked', async () => { const component = mount(); + await actWithTimeout(async () => { + await component.update(); + }); findTestSubject(component, 'vislibToggleLegend').simulate('click'); await act(async () => { expect(component.find(Settings).prop('showLegend')).toEqual(false); diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/icons/donut.tsx b/src/plugins/chart_expressions/expression_partition_vis/public/icons/donut.tsx index 5846fe0e7e8ba..3101164bfe88d 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/icons/donut.tsx +++ b/src/plugins/chart_expressions/expression_partition_vis/public/icons/donut.tsx @@ -8,25 +8,30 @@ import React from 'react'; import { EuiIconProps } from '@elastic/eui'; +import { useCommonChartStyles } from '@kbn/charts-plugin/public'; -export const DonutIcon = ({ title, titleId, ...props }: Omit) => ( - - {title ? {title} : null} - - - -); +export const DonutIcon = ({ title, titleId, ...props }: Omit) => { + const { chartIcon } = useCommonChartStyles(); + + return ( + + {title ? {title} : null} + + + + ); +}; diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/icons/mosaic.tsx b/src/plugins/chart_expressions/expression_partition_vis/public/icons/mosaic.tsx index f8582495f2e0c..1f5c99702f869 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/icons/mosaic.tsx +++ b/src/plugins/chart_expressions/expression_partition_vis/public/icons/mosaic.tsx @@ -8,25 +8,30 @@ import React from 'react'; import type { EuiIconProps } from '@elastic/eui'; +import { useCommonChartStyles } from '@kbn/charts-plugin/public'; -export const MosaicIcon = ({ title, titleId, ...props }: Omit) => ( - - {title ? : null} - - - -); +export const MosaicIcon = ({ title, titleId, ...props }: Omit) => { + const { chartIcon } = useCommonChartStyles(); + + return ( + + {title ? : null} + + + + ); +}; diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/icons/pie.tsx b/src/plugins/chart_expressions/expression_partition_vis/public/icons/pie.tsx index 9176ac3fdd5c1..5824b8f40b6e6 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/icons/pie.tsx +++ b/src/plugins/chart_expressions/expression_partition_vis/public/icons/pie.tsx @@ -8,25 +8,30 @@ import React from 'react'; import { EuiIconProps } from '@elastic/eui'; +import { useCommonChartStyles } from '@kbn/charts-plugin/public'; -export const PieIcon = ({ title, titleId, ...props }: Omit) => ( - - {title ? {title} : null} - - - -); +export const PieIcon = ({ title, titleId, ...props }: Omit) => { + const { chartIcon } = useCommonChartStyles(); + + return ( + + {title ? {title} : null} + + + + ); +}; diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/icons/treemap.tsx b/src/plugins/chart_expressions/expression_partition_vis/public/icons/treemap.tsx index 1860132fa9ffd..781b79505384d 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/icons/treemap.tsx +++ b/src/plugins/chart_expressions/expression_partition_vis/public/icons/treemap.tsx @@ -8,29 +8,34 @@ import React from 'react'; import { EuiIconProps } from '@elastic/eui'; +import { useCommonChartStyles } from '@kbn/charts-plugin/public'; -export const TreemapIcon = ({ title, titleId, ...props }: Omit) => ( - - {title ? {title} : null} - - - - -); +export const TreemapIcon = ({ title, titleId, ...props }: Omit) => { + const { chartIcon } = useCommonChartStyles(); + + return ( + + {title ? {title} : null} + + + + + ); +}; diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/icons/waffle.tsx b/src/plugins/chart_expressions/expression_partition_vis/public/icons/waffle.tsx index 30f05dd57f348..21e26facd8e15 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/icons/waffle.tsx +++ b/src/plugins/chart_expressions/expression_partition_vis/public/icons/waffle.tsx @@ -8,25 +8,30 @@ import React from 'react'; import type { EuiIconProps } from '@elastic/eui'; +import { useCommonChartStyles } from '@kbn/charts-plugin/public'; -export const WaffleIcon = ({ title, titleId, ...props }: Omit) => ( - - {title ? : null} - - - -); +export const WaffleIcon = ({ title, titleId, ...props }: Omit) => { + const { chartIcon } = useCommonChartStyles(); + + return ( + + {title ? : null} + + + + ); +}; diff --git a/src/plugins/charts/common/constants.ts b/src/plugins/charts/common/constants.ts index 01fdb5e545bb4..6d895dcba26ed 100644 --- a/src/plugins/charts/common/constants.ts +++ b/src/plugins/charts/common/constants.ts @@ -20,3 +20,23 @@ export const paletteIds = [ 'warm', 'gray', ]; + +// This set of defaults originated in Canvas, which, at present, is the primary +// consumer of this function. Changing this default requires a change in Canvas +// logic, which would likely be a breaking change in 7.x. +export const defaultCustomColors = [ + '#882E72', + '#B178A6', + '#D6C1DE', + '#1965B0', + '#5289C7', + '#7BAFDE', + '#4EB265', + '#90C987', + '#CAE0AB', + '#F7EE55', + '#F6C141', + '#F1932D', + '#E8601C', + '#DC050C', +]; diff --git a/src/plugins/charts/common/expressions/palette/index.ts b/src/plugins/charts/common/expressions/palette/index.ts new file mode 100644 index 0000000000000..cb03733a15df5 --- /dev/null +++ b/src/plugins/charts/common/expressions/palette/index.ts @@ -0,0 +1,18 @@ +/* + * 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. + */ + +export { palette } from './palette'; + +export { systemPalette } from './system_palette'; +export type { SystemPaletteArguments } from './system_palette'; + +export type { + CustomPaletteArguments, + CustomPaletteState, + PaletteExpressionFunctionDefinition, +} from './types'; diff --git a/src/plugins/charts/common/palette.test.ts b/src/plugins/charts/common/expressions/palette/palette.test.ts similarity index 54% rename from src/plugins/charts/common/palette.test.ts rename to src/plugins/charts/common/expressions/palette/palette.test.ts index b2f26db26757f..771e26ab4aa48 100644 --- a/src/plugins/charts/common/palette.test.ts +++ b/src/plugins/charts/common/expressions/palette/palette.test.ts @@ -6,14 +6,9 @@ * Side Public License, v 1. */ import type { PaletteOutput } from '@kbn/coloring'; -import { - palette, - defaultCustomColors, - systemPalette, - CustomPaletteState, - CustomPaletteArguments, -} from './palette'; import { functionWrapper } from '@kbn/expressions-plugin/common/expression_functions/specs/tests/utils'; +import { palette, CustomPaletteState, CustomPaletteArguments } from '.'; +import { defaultCustomColors } from '../../constants'; describe('palette', () => { const fn = functionWrapper(palette()) as ( @@ -21,70 +16,70 @@ describe('palette', () => { args?: Partial ) => PaletteOutput; - it('results a palette', () => { - const result = fn(null); + it('results a palette', async () => { + const result = await fn(null); expect(result).toHaveProperty('type', 'palette'); }); describe('args', () => { describe('color', () => { - it('sets colors', () => { - const result = fn(null, { color: ['red', 'green', 'blue'] }); + it('sets colors', async () => { + const result = await fn(null, { color: ['red', 'green', 'blue'] }); expect(result.params!.colors).toEqual(['red', 'green', 'blue']); }); - it('defaults to pault_tor_14 colors', () => { - const result = fn(null); + it('defaults to pault_tor_14 colors', async () => { + const result = await fn(null); expect(result.params!.colors).toEqual(defaultCustomColors); }); }); describe('stop', () => { - it('sets stops', () => { - const result = fn(null, { color: ['red', 'green', 'blue'], stop: [1, 2, 3] }); + it('sets stops', async () => { + const result = await fn(null, { color: ['red', 'green', 'blue'], stop: [1, 2, 3] }); expect(result.params!.stops).toEqual([1, 2, 3]); }); - it('defaults to pault_tor_14 colors', () => { - const result = fn(null); + it('defaults to pault_tor_14 colors', async () => { + const result = await fn(null); expect(result.params!.colors).toEqual(defaultCustomColors); }); }); describe('gradient', () => { - it('sets gradient', () => { - let result = fn(null, { gradient: true }); + it('sets gradient', async () => { + let result = await fn(null, { gradient: true }); expect(result.params).toHaveProperty('gradient', true); - result = fn(null, { gradient: false }); + result = await fn(null, { gradient: false }); expect(result.params).toHaveProperty('gradient', false); }); - it('defaults to false', () => { - const result = fn(null); + it('defaults to false', async () => { + const result = await fn(null); expect(result.params).toHaveProperty('gradient', false); }); }); describe('reverse', () => { - it('reverses order of the colors', () => { - const result = fn(null, { reverse: true }); + it('reverses order of the colors', async () => { + const result = await fn(null, { reverse: true }); expect(result.params!.colors).toEqual(defaultCustomColors.reverse()); }); - it('keeps the original order of the colors', () => { - const result = fn(null, { reverse: false }); + it('keeps the original order of the colors', async () => { + const result = await fn(null, { reverse: false }); expect(result.params!.colors).toEqual(defaultCustomColors); }); - it(`defaults to 'false`, () => { - const result = fn(null); + it(`defaults to 'false`, async () => { + const result = await fn(null); expect(result.params!.colors).toEqual(defaultCustomColors); }); - it('keeps the stops order pristine when set', () => { + it('keeps the stops order pristine when set', async () => { const stops = [1, 2, 3]; - const result = fn(null, { + const result = await fn(null, { color: ['red', 'green', 'blue'], stop: [1, 2, 3], reverse: true, @@ -94,20 +89,3 @@ describe('palette', () => { }); }); }); - -describe('system_palette', () => { - const fn = functionWrapper(systemPalette()) as ( - context: null, - args: { name: string; params?: unknown } - ) => PaletteOutput; - - it('results a palette', () => { - const result = fn(null, { name: 'test' }); - expect(result).toHaveProperty('type', 'palette'); - }); - - it('returns the name', () => { - const result = fn(null, { name: 'test' }); - expect(result).toHaveProperty('name', 'test'); - }); -}); diff --git a/src/plugins/charts/common/expressions/palette/palette.ts b/src/plugins/charts/common/expressions/palette/palette.ts new file mode 100644 index 0000000000000..1875f89020963 --- /dev/null +++ b/src/plugins/charts/common/expressions/palette/palette.ts @@ -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 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 { i18n } from '@kbn/i18n'; +import type { PaletteExpressionFunctionDefinition } from './types'; + +export function palette(): PaletteExpressionFunctionDefinition { + return { + name: 'palette', + aliases: [], + type: 'palette', + inputTypes: ['null'], + help: i18n.translate('charts.functions.paletteHelpText', { + defaultMessage: 'Creates a color palette.', + }), + args: { + color: { + aliases: ['_'], + multi: true, + types: ['string'], + help: i18n.translate('charts.functions.palette.args.colorHelpText', { + defaultMessage: + 'The palette colors. Accepts an {html} color name, {hex}, {hsl}, {hsla}, {rgb}, or {rgba}.', + values: { + html: 'HTML', + rgb: 'RGB', + rgba: 'RGBA', + hex: 'HEX', + hsl: 'HSL', + hsla: 'HSLA', + }, + }), + required: false, + }, + stop: { + multi: true, + types: ['number'], + help: i18n.translate('charts.functions.palette.args.stopHelpText', { + defaultMessage: + 'The palette color stops. When used, it must be associated with each color.', + }), + required: false, + }, + continuity: { + types: ['string'], + options: ['above', 'below', 'all', 'none'], + default: 'above', + help: '', + }, + rangeMin: { + types: ['number'], + help: '', + }, + rangeMax: { + types: ['number'], + help: '', + }, + range: { + types: ['string'], + options: ['number', 'percent'], + default: 'percent', + help: '', + }, + gradient: { + types: ['boolean'], + default: false, + help: i18n.translate('charts.functions.palette.args.gradientHelpText', { + defaultMessage: 'Make a gradient palette where supported?', + }), + options: [true, false], + }, + reverse: { + types: ['boolean'], + default: false, + help: i18n.translate('charts.functions.palette.args.reverseHelpText', { + defaultMessage: 'Reverse the palette?', + }), + options: [true, false], + }, + }, + async fn(...args) { + /** Build optimization: prevent adding extra code into initial bundle **/ + const { paletteExpressionFn } = await import('./palette_fn'); + return paletteExpressionFn(...args); + }, + }; +} diff --git a/src/plugins/charts/common/expressions/palette/palette_fn.ts b/src/plugins/charts/common/expressions/palette/palette_fn.ts new file mode 100644 index 0000000000000..1702c95ab76e7 --- /dev/null +++ b/src/plugins/charts/common/expressions/palette/palette_fn.ts @@ -0,0 +1,53 @@ +/* + * 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 { last } from 'lodash'; +import { checkIsMaxContinuity, checkIsMinContinuity } from '@kbn/coloring'; +import type { PaletteExpressionFunctionDefinition } from './types'; +import { defaultCustomColors } from '../../constants'; + +export const paletteExpressionFn: PaletteExpressionFunctionDefinition['fn'] = async ( + input, + args +) => { + const { color, continuity, reverse, gradient, stop, range, rangeMin, rangeMax } = args; + const colors = ([] as string[]).concat(color || defaultCustomColors); + const stops = ([] as number[]).concat(stop || []); + if (stops.length > 0 && colors.length !== stops.length) { + throw Error('When stop is used, each color must have an associated stop value.'); + } + + // If the user has defined stops, choose rangeMin/Max, provided by user or range, + // taken from first/last element of ranges or default range (0 or 100). + const calculateRange = ( + userRange: number | undefined, + stopsRange: number | undefined, + defaultRange: number + ) => userRange ?? stopsRange ?? defaultRange; + + const rangeMinDefault = 0; + const rangeMaxDefault = 100; + + return { + type: 'palette', + name: 'custom', + params: { + colors: reverse ? colors.reverse() : colors, + stops, + range: range ?? 'percent', + gradient, + continuity, + rangeMin: checkIsMinContinuity(continuity) + ? Number.NEGATIVE_INFINITY + : calculateRange(rangeMin, stops[0], rangeMinDefault), + rangeMax: checkIsMaxContinuity(continuity) + ? Number.POSITIVE_INFINITY + : calculateRange(rangeMax, last(stops), rangeMaxDefault), + }, + }; +}; diff --git a/src/plugins/charts/common/expressions/palette/system_palette.test.ts b/src/plugins/charts/common/expressions/palette/system_palette.test.ts new file mode 100644 index 0000000000000..82c55f99723ea --- /dev/null +++ b/src/plugins/charts/common/expressions/palette/system_palette.test.ts @@ -0,0 +1,27 @@ +/* + * 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 type { PaletteOutput } from '@kbn/coloring'; +import { functionWrapper } from '@kbn/expressions-plugin/common/expression_functions/specs/tests/utils'; +import { systemPalette } from './system_palette'; + +describe('system_palette', () => { + const fn = functionWrapper(systemPalette()) as ( + context: null, + args: { name: string; params?: unknown } + ) => PaletteOutput; + + it('results a palette', () => { + const result = fn(null, { name: 'test' }); + expect(result).toHaveProperty('type', 'palette'); + }); + + it('returns the name', () => { + const result = fn(null, { name: 'test' }); + expect(result).toHaveProperty('name', 'test'); + }); +}); diff --git a/src/plugins/charts/common/expressions/palette/system_palette.ts b/src/plugins/charts/common/expressions/palette/system_palette.ts new file mode 100644 index 0000000000000..e4bfed538570e --- /dev/null +++ b/src/plugins/charts/common/expressions/palette/system_palette.ts @@ -0,0 +1,48 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import type { ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; +import type { PaletteOutput } from './types'; +import { paletteIds } from '../../constants'; + +export interface SystemPaletteArguments { + name: string; +} + +export function systemPalette(): ExpressionFunctionDefinition< + 'system_palette', + null, + SystemPaletteArguments, + PaletteOutput +> { + return { + name: 'system_palette', + aliases: [], + type: 'palette', + inputTypes: ['null'], + help: i18n.translate('charts.functions.systemPaletteHelpText', { + defaultMessage: 'Creates a dynamic color palette.', + }), + args: { + name: { + types: ['string'], + help: i18n.translate('charts.functions.systemPalette.args.nameHelpText', { + defaultMessage: 'Name of the palette in the palette list', + }), + options: paletteIds, + }, + }, + fn: (input, args) => { + return { + type: 'palette', + name: args.name, + }; + }, + }; +} diff --git a/src/plugins/charts/common/expressions/palette/types.ts b/src/plugins/charts/common/expressions/palette/types.ts new file mode 100644 index 0000000000000..12fef842674f1 --- /dev/null +++ b/src/plugins/charts/common/expressions/palette/types.ts @@ -0,0 +1,43 @@ +/* + * 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 type { PaletteContinuity } from '@kbn/coloring'; +import type { ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; + +export interface PaletteOutput { + type: 'palette' | 'system_palette'; + name: string; + params?: T; +} + +export interface CustomPaletteArguments { + color?: string[]; + gradient: boolean; + reverse?: boolean; + stop?: number[]; + range?: 'number' | 'percent'; + rangeMin?: number; + rangeMax?: number; + continuity?: PaletteContinuity; +} + +export interface CustomPaletteState { + colors: string[]; + gradient: boolean; + stops: number[]; + range: 'number' | 'percent'; + rangeMin: number; + rangeMax: number; + continuity?: PaletteContinuity; +} + +export type PaletteExpressionFunctionDefinition = ExpressionFunctionDefinition< + 'palette', + null, + CustomPaletteArguments, + Promise> +>; diff --git a/src/plugins/charts/common/index.ts b/src/plugins/charts/common/index.ts index 5ad9eb264e78b..74389ce43990f 100644 --- a/src/plugins/charts/common/index.ts +++ b/src/plugins/charts/common/index.ts @@ -9,11 +9,14 @@ export const COLOR_MAPPING_SETTING = 'visualization:colorMapping'; export const LEGACY_TIME_AXIS = 'visualization:useLegacyTimeAxis'; -export type { CustomPaletteArguments, CustomPaletteState, SystemPaletteArguments } from './palette'; +export type { + CustomPaletteArguments, + CustomPaletteState, + SystemPaletteArguments, +} from './expressions/palette'; +export { palette, systemPalette } from './expressions/palette'; -export { defaultCustomColors, palette, systemPalette } from './palette'; - -export { paletteIds } from './constants'; +export { paletteIds, defaultCustomColors } from './constants'; export type { ColorSchema, RawColorSchema, ColorMap } from './static'; export { ColorSchemas, @@ -29,11 +32,3 @@ export { } from './static'; export type { ColorSchemaParams, Labels, Style } from './types'; - -/** @deprecated **/ -/** Please import directly from @kbn/coloring **/ -export { checkIsMinContinuity, checkIsMaxContinuity } from '@kbn/coloring'; - -/** @deprecated **/ -/** Please import directly from @kbn/coloring **/ -export type { PaletteOutput, PaletteContinuity } from '@kbn/coloring'; diff --git a/src/plugins/charts/common/palette.ts b/src/plugins/charts/common/palette.ts deleted file mode 100644 index ebb4d3dff9e59..0000000000000 --- a/src/plugins/charts/common/palette.ts +++ /dev/null @@ -1,216 +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 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 type { ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; -import type { PaletteContinuity } from '@kbn/coloring'; -import { i18n } from '@kbn/i18n'; -import { checkIsMaxContinuity, checkIsMinContinuity } from '@kbn/coloring'; -import { last } from 'lodash'; -import { paletteIds } from './constants'; - -export interface CustomPaletteArguments { - color?: string[]; - gradient: boolean; - reverse?: boolean; - stop?: number[]; - range?: 'number' | 'percent'; - rangeMin?: number; - rangeMax?: number; - continuity?: PaletteContinuity; -} - -export interface CustomPaletteState { - colors: string[]; - gradient: boolean; - stops: number[]; - range: 'number' | 'percent'; - rangeMin: number; - rangeMax: number; - continuity?: PaletteContinuity; -} - -export interface SystemPaletteArguments { - name: string; -} - -export interface PaletteOutput { - type: 'palette' | 'system_palette'; - name: string; - params?: T; -} - -export const defaultCustomColors = [ - // This set of defaults originated in Canvas, which, at present, is the primary - // consumer of this function. Changing this default requires a change in Canvas - // logic, which would likely be a breaking change in 7.x. - '#882E72', - '#B178A6', - '#D6C1DE', - '#1965B0', - '#5289C7', - '#7BAFDE', - '#4EB265', - '#90C987', - '#CAE0AB', - '#F7EE55', - '#F6C141', - '#F1932D', - '#E8601C', - '#DC050C', -]; - -export function palette(): ExpressionFunctionDefinition< - 'palette', - null, - CustomPaletteArguments, - PaletteOutput -> { - return { - name: 'palette', - aliases: [], - type: 'palette', - inputTypes: ['null'], - help: i18n.translate('charts.functions.paletteHelpText', { - defaultMessage: 'Creates a color palette.', - }), - args: { - color: { - aliases: ['_'], - multi: true, - types: ['string'], - help: i18n.translate('charts.functions.palette.args.colorHelpText', { - defaultMessage: - 'The palette colors. Accepts an {html} color name, {hex}, {hsl}, {hsla}, {rgb}, or {rgba}.', - values: { - html: 'HTML', - rgb: 'RGB', - rgba: 'RGBA', - hex: 'HEX', - hsl: 'HSL', - hsla: 'HSLA', - }, - }), - required: false, - }, - stop: { - multi: true, - types: ['number'], - help: i18n.translate('charts.functions.palette.args.stopHelpText', { - defaultMessage: - 'The palette color stops. When used, it must be associated with each color.', - }), - required: false, - }, - continuity: { - types: ['string'], - options: ['above', 'below', 'all', 'none'], - default: 'above', - help: '', - }, - rangeMin: { - types: ['number'], - help: '', - }, - rangeMax: { - types: ['number'], - help: '', - }, - range: { - types: ['string'], - options: ['number', 'percent'], - default: 'percent', - help: '', - }, - gradient: { - types: ['boolean'], - default: false, - help: i18n.translate('charts.functions.palette.args.gradientHelpText', { - defaultMessage: 'Make a gradient palette where supported?', - }), - options: [true, false], - }, - reverse: { - types: ['boolean'], - default: false, - help: i18n.translate('charts.functions.palette.args.reverseHelpText', { - defaultMessage: 'Reverse the palette?', - }), - options: [true, false], - }, - }, - fn: (input, args) => { - const { color, continuity, reverse, gradient, stop, range, rangeMin, rangeMax } = args; - const colors = ([] as string[]).concat(color || defaultCustomColors); - const stops = ([] as number[]).concat(stop || []); - if (stops.length > 0 && colors.length !== stops.length) { - throw Error('When stop is used, each color must have an associated stop value.'); - } - - // If the user has defined stops, choose rangeMin/Max, provided by user or range, - // taken from first/last element of ranges or default range (0 or 100). - const calculateRange = ( - userRange: number | undefined, - stopsRange: number | undefined, - defaultRange: number - ) => userRange ?? stopsRange ?? defaultRange; - - const rangeMinDefault = 0; - const rangeMaxDefault = 100; - - return { - type: 'palette', - name: 'custom', - params: { - colors: reverse ? colors.reverse() : colors, - stops, - range: range ?? 'percent', - gradient, - continuity, - rangeMin: checkIsMinContinuity(continuity) - ? Number.NEGATIVE_INFINITY - : calculateRange(rangeMin, stops[0], rangeMinDefault), - rangeMax: checkIsMaxContinuity(continuity) - ? Number.POSITIVE_INFINITY - : calculateRange(rangeMax, last(stops), rangeMaxDefault), - }, - }; - }, - }; -} - -export function systemPalette(): ExpressionFunctionDefinition< - 'system_palette', - null, - SystemPaletteArguments, - PaletteOutput -> { - return { - name: 'system_palette', - aliases: [], - type: 'palette', - inputTypes: ['null'], - help: i18n.translate('charts.functions.systemPaletteHelpText', { - defaultMessage: 'Creates a dynamic color palette.', - }), - args: { - name: { - types: ['string'], - help: i18n.translate('charts.functions.systemPalette.args.nameHelpText', { - defaultMessage: 'Name of the palette in the palette list', - }), - options: paletteIds, - }, - }, - fn: (input, args) => { - return { - type: 'palette', - name: args.name, - }; - }, - }; -} diff --git a/src/plugins/charts/public/index.ts b/src/plugins/charts/public/index.ts index 666bd1cc9ad4f..d6cae90d50057 100644 --- a/src/plugins/charts/public/index.ts +++ b/src/plugins/charts/public/index.ts @@ -42,7 +42,3 @@ export { LabelRotation, defaultCountLabel, } from '../common'; - -/** @deprecated **/ -/** Please import directly from @kbn/coloring **/ -export type { SeriesLayer, PaletteRegistry, PaletteOutput, PaletteDefinition } from '@kbn/coloring'; diff --git a/src/plugins/charts/public/static/components/color_picker.scss b/src/plugins/charts/public/static/components/color_picker.scss deleted file mode 100644 index 5def2b75a4c50..0000000000000 --- a/src/plugins/charts/public/static/components/color_picker.scss +++ /dev/null @@ -1,30 +0,0 @@ -$visColorPickerWidth: $euiSizeL * 8; // 8 columns - -.visColorPicker__value { - width: $visColorPickerWidth; -} - -.visColorPicker__colorBtn { - position: relative; - - input[type='radio'] { - position: absolute; - top: 50%; - left: 50%; - opacity: 0; - transform: translate(-50%, -50%); - } -} - -.visColorPicker__valueDot { - cursor: pointer; - - &:hover { - transform: scale(1.4); - } - - &-isSelected { - border: $euiSizeXS solid; - border-radius: 100%; - } -} diff --git a/src/plugins/charts/public/static/components/color_picker.tsx b/src/plugins/charts/public/static/components/color_picker.tsx index 25c210b87a0e1..edd68ce6dbbba 100644 --- a/src/plugins/charts/public/static/components/color_picker.tsx +++ b/src/plugins/charts/public/static/components/color_picker.tsx @@ -6,9 +6,8 @@ * Side Public License, v 1. */ -import classNames from 'classnames'; -import React, { BaseSyntheticEvent } from 'react'; - +import React, { BaseSyntheticEvent, useMemo } from 'react'; +import { css } from '@emotion/react'; import { EuiButtonEmpty, EuiFlexItem, @@ -16,10 +15,10 @@ import { euiPaletteColorBlind, EuiScreenReaderOnly, EuiFlexGroup, + useEuiTheme, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { lightenColor } from '../../services/palettes/lighten_color'; -import './color_picker.scss'; export const legacyColors: string[] = [ '#3F6833', @@ -80,7 +79,7 @@ export const legacyColors: string[] = [ '#DEDAF7', ]; -interface ColorPickerProps { +export interface ColorPickerProps { /** * Label that characterizes the color that is going to change */ @@ -116,6 +115,24 @@ interface ColorPickerProps { } const euiColors = euiPaletteColorBlind({ rotations: 4, order: 'group' }); +const visColorPickerColorBtnStyle = css` + position: relative; + input[type='radio'] { + position: absolute; + top: 50%; + left: 50%; + opacity: 0; + transform: translate(-50%, -50%); + } +`; + +const visColorPickerValueDotStyle = css` + cursor: pointer; + &:hover { + transform: scale(1.4); + } +`; + export const ColorPicker = ({ onChange, color: selectedColor, @@ -127,6 +144,22 @@ export const ColorPicker = ({ layerIndex, }: ColorPickerProps) => { const legendColors = useLegacyColors ? legacyColors : euiColors; + const { euiTheme } = useEuiTheme(); + + const visColorPickerValueStyle = useMemo( + () => css` + width: calc(${euiTheme.size.l} * 8); + `, + [euiTheme.size.l] + ); + + const visColorPickerValueDotSelectedStyle = useMemo( + () => css` + border: ${euiTheme.size.xs} solid; + border-radius: 100%; + `, + [euiTheme.size.xs] + ); return (
@@ -140,9 +173,18 @@ export const ColorPicker = ({ /> - + {legendColors.map((color) => ( -