[Step 3] Cleanup charts plugin (#130132)

* [Step 3] Cleanup charts plugin

* update JEST

* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Alexey Antonov 2022-04-19 11:47:37 +05:00 committed by GitHub
parent 2d188508d5
commit f2af25db45
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 802 additions and 581 deletions

View file

@ -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(

View file

@ -1,8 +0,0 @@
.lnsPalettePanel__section {
padding: $euiSize;
}
.lnsPalettePanel__section--shaded {
background-color: $euiColorLightestShade;
border-bottom: $euiBorderThin;
}

View file

@ -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 (
<div className="lnsPalettePanel__section lnsPalettePanel__section--shaded">
<div css={styles} className="lnsPalettePanel__section">
<EuiFormRow
display="rowCompressed"
label={i18n.translate('coloring.dynamicColoring.palettePicker.label', {

View file

@ -3,7 +3,7 @@ pageLoadAssetSize:
alerting: 106936
apm: 64385
canvas: 1066647
charts: 95000
charts: 55000
cloud: 21076
console: 46091
core: 435325

View file

@ -8,25 +8,30 @@
import React from 'react';
import type { EuiIconProps } from '@elastic/eui';
import { useCommonChartStyles } from '@kbn/charts-plugin/public';
export const HorizontalBulletIcon = ({ title, titleId, ...props }: Omit<EuiIconProps, 'type'>) => (
<svg
width="30"
height="22"
viewBox="0 0 30 22"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-labelledby={titleId}
{...props}
>
{title ? <title id={titleId}>{title}</title> : null}
<path
className="chart-icon__subdued"
d="M1 13a1 1 0 00-1 1v2a1 1 0 102 0v-1h5v1a1 1 0 102 0v-1h5v1a1 1 0 102 0v-1h5v1a1 1 0 102 0v-1h5v1a1 1 0 102 0v-2a1 1 0 00-1-1H1z"
/>
<path
className="chart-icon__accent"
d="M0 6a1 1 0 011-1h24a1 1 0 011 1v4a1 1 0 01-1 1H1a1 1 0 01-1-1V6z"
/>
</svg>
);
export const HorizontalBulletIcon = ({ title, titleId, ...props }: Omit<EuiIconProps, 'type'>) => {
const { chartIcon } = useCommonChartStyles();
return (
<svg
width="30"
height="22"
viewBox="0 0 30 22"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-labelledby={titleId}
{...props}
>
{title ? <title id={titleId}>{title}</title> : null}
<path
css={chartIcon.subdued}
d="M1 13a1 1 0 00-1 1v2a1 1 0 102 0v-1h5v1a1 1 0 102 0v-1h5v1a1 1 0 102 0v-1h5v1a1 1 0 102 0v-1h5v1a1 1 0 102 0v-2a1 1 0 00-1-1H1z"
/>
<path
css={chartIcon.accent}
d="M0 6a1 1 0 011-1h24a1 1 0 011 1v4a1 1 0 01-1 1H1a1 1 0 01-1-1V6z"
/>
</svg>
);
};

View file

@ -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<EuiIconProps, 'type'>) => (
<svg
width="30"
height="22"
viewBox="0 0 30 22"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-labelledby={titleId}
{...props}
>
{title ? <title id={titleId}>{title}</title> : null}
<path
className="chart-icon__subdued"
d="M16 22a1 1 0 01-1-1V4a1 1 0 011-1h4a1 1 0 011 1v17a1 1 0 01-1 1h-4z"
/>
<path
className="chart-icon__accent"
d="M10 0h2a1 1 0 011 1v20a1 1 0 01-1 1h-2a1 1 0 110-2h1v-3h-1a1 1 0 110-2h1v-3h-1a1 1 0 110-2h1V7h-1a1 1 0 010-2h1V2h-1a1 1 0 010-2z"
/>
</svg>
);
export const VerticalBulletIcon = ({ title, titleId, ...props }: Omit<EuiIconProps, 'type'>) => {
const { chartIcon } = useCommonChartStyles();
return (
<svg
width="30"
height="22"
viewBox="0 0 30 22"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-labelledby={titleId}
{...props}
>
{title ? <title id={titleId}>{title}</title> : null}
<path
css={chartIcon.subdued}
d="M16 22a1 1 0 01-1-1V4a1 1 0 011-1h4a1 1 0 011 1v17a1 1 0 01-1 1h-4z"
/>
<path
css={chartIcon.accent}
d="M10 0h2a1 1 0 011 1v20a1 1 0 01-1 1h-2a1 1 0 110-2h1v-3h-1a1 1 0 110-2h1v-3h-1a1 1 0 110-2h1V7h-1a1 1 0 010-2h1V2h-1a1 1 0 010-2z"
/>
</svg>
);
};

View file

@ -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(<HeatmapComponent {...wrapperProps} />);
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(<HeatmapComponent {...newProps} />);
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(<HeatmapComponent {...wrapperProps} />);
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(<HeatmapComponent {...newProps} />);
await actWithTimeout(async () => {
await component.update();
});
await act(async () => {
expect(component.find(Settings).prop('legendColorPicker')).toBeUndefined();
});

View file

@ -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<EuiIconProps, 'type'>) => (
<svg
width={30}
height={22}
viewBox="0 0 30 22"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-labelledby={titleId}
{...props}
>
{title ? <title id={titleId}>{title}</title> : null}
<path
className="chart-icon__accent"
d="M16 1a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1h-4a1 1 0 01-1-1V1zM0 17a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1H1a1 1 0 01-1-1v-4zm17-9a1 1 0 00-1 1v4a1 1 0 001 1h4a1 1 0 001-1V9a1 1 0 00-1-1h-4zm-1 9a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1h-4a1 1 0 01-1-1v-4zm9-17a1 1 0 00-1 1v4a1 1 0 001 1h4a1 1 0 001-1V1a1 1 0 00-1-1h-4z"
/>
<path
className="chart-icon__subdued"
d="M0 1a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1H1a1 1 0 01-1-1V1zm0 8a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1H1a1 1 0 01-1-1V9zm9-9a1 1 0 00-1 1v4a1 1 0 001 1h4a1 1 0 001-1V1a1 1 0 00-1-1H9zM8 9a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1H9a1 1 0 01-1-1V9zm1 7a1 1 0 00-1 1v4a1 1 0 001 1h4a1 1 0 001-1v-4a1 1 0 00-1-1H9zm15-7a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1h-4a1 1 0 01-1-1V9zm1 7a1 1 0 00-1 1v4a1 1 0 001 1h4a1 1 0 001-1v-4a1 1 0 00-1-1h-4z"
/>
</svg>
);
export const HeatmapIcon = ({ title, titleId, ...props }: Omit<EuiIconProps, 'type'>) => {
const { chartIcon } = useCommonChartStyles();
return (
<svg
width={30}
height={22}
viewBox="0 0 30 22"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-labelledby={titleId}
{...props}
>
{title ? <title id={titleId}>{title}</title> : null}
<path
css={chartIcon.subdued}
d="M16 1a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1h-4a1 1 0 01-1-1V1zM0 17a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1H1a1 1 0 01-1-1v-4zm17-9a1 1 0 00-1 1v4a1 1 0 001 1h4a1 1 0 001-1V9a1 1 0 00-1-1h-4zm-1 9a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1h-4a1 1 0 01-1-1v-4zm9-17a1 1 0 00-1 1v4a1 1 0 001 1h4a1 1 0 001-1V1a1 1 0 00-1-1h-4z"
/>
<path
css={chartIcon.accent}
d="M0 1a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1H1a1 1 0 01-1-1V1zm0 8a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1H1a1 1 0 01-1-1V9zm9-9a1 1 0 00-1 1v4a1 1 0 001 1h4a1 1 0 001-1V1a1 1 0 00-1-1H9zM8 9a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1H9a1 1 0 01-1-1V9zm1 7a1 1 0 00-1 1v4a1 1 0 001 1h4a1 1 0 001-1v-4a1 1 0 00-1-1H9zm15-7a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1h-4a1 1 0 01-1-1V9zm1 7a1 1 0 00-1 1v4a1 1 0 001 1h4a1 1 0 001-1v-4a1 1 0 00-1-1h-4z"
/>
</svg>
);
};

View file

@ -228,7 +228,7 @@ exports[`PartitionVisComponent should render correct structure for donut 1`] = `
}
}
>
<Memo(LegendToggleComponent)
<ForwardRef
legendPosition="right"
onClick={[Function]}
showLegend={true}
@ -656,7 +656,7 @@ exports[`PartitionVisComponent should render correct structure for mosaic 1`] =
}
}
>
<Memo(LegendToggleComponent)
<ForwardRef
legendPosition="right"
onClick={[Function]}
showLegend={true}
@ -1035,7 +1035,7 @@ exports[`PartitionVisComponent should render correct structure for pie 1`] = `
}
}
>
<Memo(LegendToggleComponent)
<ForwardRef
legendPosition="right"
onClick={[Function]}
showLegend={true}
@ -1447,7 +1447,7 @@ exports[`PartitionVisComponent should render correct structure for treemap 1`] =
}
}
>
<Memo(LegendToggleComponent)
<ForwardRef
legendPosition="right"
onClick={[Function]}
showLegend={true}
@ -1847,7 +1847,7 @@ exports[`PartitionVisComponent should render correct structure for waffle 1`] =
}
}
>
<Memo(LegendToggleComponent)
<ForwardRef
legendPosition="right"
onClick={[Function]}
showLegend={true}

View file

@ -34,6 +34,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 visParams = createMockPieParams();
@ -131,13 +142,12 @@ describe('PartitionVisComponent', function () {
expect(component).toMatchSnapshot();
});
it('renders the legend on the correct position', () => {
const component = shallow(<PartitionVisComponent {...wrapperProps} />);
expect(component.find(Settings).prop('legendPosition')).toEqual('right');
});
it('renders the legend toggle component', async () => {
const component = mount(<PartitionVisComponent {...wrapperProps} />);
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(<PartitionVisComponent {...wrapperProps} />);
await actWithTimeout(async () => {
await component.update();
});
findTestSubject(component, 'vislibToggleLegend').simulate('click');
await act(async () => {
expect(component.find(Settings).prop('showLegend')).toEqual(false);

View file

@ -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<EuiIconProps, 'type'>) => (
<svg
viewBox="0 0 30 22"
width={30}
height={22}
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-labelledby={titleId}
{...props}
>
{title ? <title id={titleId}>{title}</title> : null}
<path
d="M19.21 21.119a11 11 0 006.595-8.1c.11-.577-.355-1.082-.942-1.082H20.75c-.477 0-.878.342-1.046.788a5.028 5.028 0 11-6.474-6.474c.447-.168.788-.569.788-1.046V1.094c0-.588-.505-1.053-1.082-.943a11 11 0 106.272 20.968h.002z"
className="chart-icon__subdued"
/>
<path
d="M22.778 3.176A11 11 0 0017.084.154C16.507.042 16 .507 16 1.095v4.116c0 .475.34.875.784 1.044l.14.055A5.026 5.026 0 0119.7 9.17c.168.445.568.784 1.044.784h4.115c.588 0 1.053-.506.942-1.084a11 11 0 00-3.023-5.694z"
className="chart-icon__accent"
/>
</svg>
);
export const DonutIcon = ({ title, titleId, ...props }: Omit<EuiIconProps, 'type'>) => {
const { chartIcon } = useCommonChartStyles();
return (
<svg
viewBox="0 0 30 22"
width={30}
height={22}
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-labelledby={titleId}
{...props}
>
{title ? <title id={titleId}>{title}</title> : null}
<path
d="M19.21 21.119a11 11 0 006.595-8.1c.11-.577-.355-1.082-.942-1.082H20.75c-.477 0-.878.342-1.046.788a5.028 5.028 0 11-6.474-6.474c.447-.168.788-.569.788-1.046V1.094c0-.588-.505-1.053-1.082-.943a11 11 0 106.272 20.968h.002z"
css={chartIcon.subdued}
/>
<path
d="M22.778 3.176A11 11 0 0017.084.154C16.507.042 16 .507 16 1.095v4.116c0 .475.34.875.784 1.044l.14.055A5.026 5.026 0 0119.7 9.17c.168.445.568.784 1.044.784h4.115c.588 0 1.053-.506.942-1.084a11 11 0 00-3.023-5.694z"
css={chartIcon.accent}
/>
</svg>
);
};

View file

@ -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<EuiIconProps, 'type'>) => (
<svg
viewBox="0 0 30 22"
width={30}
height={22}
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-labelledby={titleId}
{...props}
>
{title ? <title id={titleId} /> : null}
<path
className="chart-icon__subdued"
d="M2 0a1 1 0 00-1 1v2a1 1 0 001 1h6a1 1 0 001-1V1a1 1 0 00-1-1H2zM2 14a1 1 0 00-1 1v6a1 1 0 001 1h6a1 1 0 001-1v-6a1 1 0 00-1-1H2zM11 13a1 1 0 011-1h6a1 1 0 011 1v8a1 1 0 01-1 1h-6a1 1 0 01-1-1v-8zM12 0a1 1 0 100 2h6a1 1 0 100-2h-6zM21 15a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1h-6a1 1 0 01-1-1v-6zM22 0a1 1 0 00-1 1v4a1 1 0 001 1h6a1 1 0 001-1V1a1 1 0 00-1-1h-6z"
/>
<path
className="chart-icon__accent"
d="M11 5a1 1 0 011-1h6a1 1 0 011 1v4a1 1 0 01-1 1h-6a1 1 0 01-1-1V5zM1 7a1 1 0 011-1h6a1 1 0 011 1v4a1 1 0 01-1 1H2a1 1 0 01-1-1V7zM22 8a1 1 0 00-1 1v2a1 1 0 001 1h6a1 1 0 001-1V9a1 1 0 00-1-1h-6z"
/>
</svg>
);
export const MosaicIcon = ({ title, titleId, ...props }: Omit<EuiIconProps, 'type'>) => {
const { chartIcon } = useCommonChartStyles();
return (
<svg
viewBox="0 0 30 22"
width={30}
height={22}
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-labelledby={titleId}
{...props}
>
{title ? <title id={titleId} /> : null}
<path
css={chartIcon.subdued}
d="M2 0a1 1 0 00-1 1v2a1 1 0 001 1h6a1 1 0 001-1V1a1 1 0 00-1-1H2zM2 14a1 1 0 00-1 1v6a1 1 0 001 1h6a1 1 0 001-1v-6a1 1 0 00-1-1H2zM11 13a1 1 0 011-1h6a1 1 0 011 1v8a1 1 0 01-1 1h-6a1 1 0 01-1-1v-8zM12 0a1 1 0 100 2h6a1 1 0 100-2h-6zM21 15a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1h-6a1 1 0 01-1-1v-6zM22 0a1 1 0 00-1 1v4a1 1 0 001 1h6a1 1 0 001-1V1a1 1 0 00-1-1h-6z"
/>
<path
css={chartIcon.accent}
d="M11 5a1 1 0 011-1h6a1 1 0 011 1v4a1 1 0 01-1 1h-6a1 1 0 01-1-1V5zM1 7a1 1 0 011-1h6a1 1 0 011 1v4a1 1 0 01-1 1H2a1 1 0 01-1-1V7zM22 8a1 1 0 00-1 1v2a1 1 0 001 1h6a1 1 0 001-1V9a1 1 0 00-1-1h-6z"
/>
</svg>
);
};

View file

@ -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<EuiIconProps, 'type'>) => (
<svg
viewBox="0 0 30 22"
width={30}
height={22}
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-labelledby={titleId}
{...props}
>
{title ? <title id={titleId}>{title}</title> : null}
<path
d="M17.827 21.189a10.001 10.001 0 005.952-7.148c.124-.578-.343-1.091-.935-1.091H14a1 1 0 01-1-1V3.106c0-.592-.513-1.059-1.092-.935a10 10 0 105.919 19.018z"
className="chart-icon__subdued"
/>
<path
d="M22.462 3.538A12.29 12.29 0 0016.094.16C15.512.048 15 .514 15 1.106V10a1 1 0 001 1h8.895c.591 0 1.057-.512.945-1.094a12.288 12.288 0 00-3.378-6.368z"
className="chart-icon__accent"
/>
</svg>
);
export const PieIcon = ({ title, titleId, ...props }: Omit<EuiIconProps, 'type'>) => {
const { chartIcon } = useCommonChartStyles();
return (
<svg
viewBox="0 0 30 22"
width={30}
height={22}
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-labelledby={titleId}
{...props}
>
{title ? <title id={titleId}>{title}</title> : null}
<path
d="M17.827 21.189a10.001 10.001 0 005.952-7.148c.124-.578-.343-1.091-.935-1.091H14a1 1 0 01-1-1V3.106c0-.592-.513-1.059-1.092-.935a10 10 0 105.919 19.018z"
css={chartIcon.subdued}
/>
<path
d="M22.462 3.538A12.29 12.29 0 0016.094.16C15.512.048 15 .514 15 1.106V10a1 1 0 001 1h8.895c.591 0 1.057-.512.945-1.094a12.288 12.288 0 00-3.378-6.368z"
css={chartIcon.accent}
/>
</svg>
);
};

View file

@ -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<EuiIconProps, 'type'>) => (
<svg
viewBox="0 0 30 22"
width={30}
height={22}
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-labelledby={titleId}
{...props}
>
{title ? <title id={titleId}>{title}</title> : null}
<path
d="M0 1a1 1 0 011-1h13a1 1 0 011 1v20a1 1 0 01-1 1H1a1 1 0 01-1-1V1z"
className="chart-icon__subdued"
/>
<path
d="M17 1a1 1 0 011-1h11a1 1 0 011 1v12a1 1 0 01-1 1H18a1 1 0 01-1-1V1z"
className="chart-icon__accent"
/>
<path
d="M29 16H18a1 1 0 00-1 1v4a1 1 0 001 1h11a1 1 0 001-1v-4a1 1 0 00-1-1z"
className="chart-icon__subdued"
/>
</svg>
);
export const TreemapIcon = ({ title, titleId, ...props }: Omit<EuiIconProps, 'type'>) => {
const { chartIcon } = useCommonChartStyles();
return (
<svg
viewBox="0 0 30 22"
width={30}
height={22}
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-labelledby={titleId}
{...props}
>
{title ? <title id={titleId}>{title}</title> : null}
<path
d="M0 1a1 1 0 011-1h13a1 1 0 011 1v20a1 1 0 01-1 1H1a1 1 0 01-1-1V1z"
css={chartIcon.subdued}
/>
<path
d="M17 1a1 1 0 011-1h11a1 1 0 011 1v12a1 1 0 01-1 1H18a1 1 0 01-1-1V1z"
css={chartIcon.accent}
/>
<path
d="M29 16H18a1 1 0 00-1 1v4a1 1 0 001 1h11a1 1 0 001-1v-4a1 1 0 00-1-1z"
css={chartIcon.subdued}
/>
</svg>
);
};

View file

@ -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<EuiIconProps, 'type'>) => (
<svg
viewBox="0 0 30 22"
width={30}
height={22}
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-labelledby={titleId}
{...props}
>
{title ? <title id={titleId} /> : null}
<path
className="chart-icon__accent"
d="M16 1a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 01-1 1h-2a1 1 0 01-1-1V1zM4 13a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1v-2zM17 6a1 1 0 00-1 1v2a1 1 0 001 1h2a1 1 0 001-1V7a1 1 0 00-1-1h-2zM23 0a1 1 0 00-1 1v2a1 1 0 001 1h2a1 1 0 001-1V1a1 1 0 00-1-1h-2zM5 0a1 1 0 00-1 1v2a1 1 0 001 1h2a1 1 0 001-1V1a1 1 0 00-1-1H5zM4 7a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V7zM11 0a1 1 0 00-1 1v2a1 1 0 001 1h2a1 1 0 001-1V1a1 1 0 00-1-1h-2zM10 7a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 01-1 1h-2a1 1 0 01-1-1V7zM11 12a1 1 0 00-1 1v2a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 00-1-1h-2zM22 7a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 01-1 1h-2a1 1 0 01-1-1V7z"
/>
<path
className="chart-icon__subdued"
d="M22 13a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 01-1 1h-2a1 1 0 01-1-1v-2zM4 19a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1v-2zM16 19a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 01-1 1h-2a1 1 0 01-1-1v-2zM11 18a1 1 0 00-1 1v2a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 00-1-1h-2zM23 18a1 1 0 00-1 1v2a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 00-1-1h-2zM16 13a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 01-1 1h-2a1 1 0 01-1-1v-2z"
/>
</svg>
);
export const WaffleIcon = ({ title, titleId, ...props }: Omit<EuiIconProps, 'type'>) => {
const { chartIcon } = useCommonChartStyles();
return (
<svg
viewBox="0 0 30 22"
width={30}
height={22}
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-labelledby={titleId}
{...props}
>
{title ? <title id={titleId} /> : null}
<path
css={chartIcon.subdued}
d="M16 1a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 01-1 1h-2a1 1 0 01-1-1V1zM4 13a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1v-2zM17 6a1 1 0 00-1 1v2a1 1 0 001 1h2a1 1 0 001-1V7a1 1 0 00-1-1h-2zM23 0a1 1 0 00-1 1v2a1 1 0 001 1h2a1 1 0 001-1V1a1 1 0 00-1-1h-2zM5 0a1 1 0 00-1 1v2a1 1 0 001 1h2a1 1 0 001-1V1a1 1 0 00-1-1H5zM4 7a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V7zM11 0a1 1 0 00-1 1v2a1 1 0 001 1h2a1 1 0 001-1V1a1 1 0 00-1-1h-2zM10 7a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 01-1 1h-2a1 1 0 01-1-1V7zM11 12a1 1 0 00-1 1v2a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 00-1-1h-2zM22 7a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 01-1 1h-2a1 1 0 01-1-1V7z"
/>
<path
css={chartIcon.accent}
d="M22 13a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 01-1 1h-2a1 1 0 01-1-1v-2zM4 19a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1v-2zM16 19a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 01-1 1h-2a1 1 0 01-1-1v-2zM11 18a1 1 0 00-1 1v2a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 00-1-1h-2zM23 18a1 1 0 00-1 1v2a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 00-1-1h-2zM16 13a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 01-1 1h-2a1 1 0 01-1-1v-2z"
/>
</svg>
);
};

View file

@ -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',
];

View file

@ -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';

View file

@ -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<CustomPaletteArguments>
) => PaletteOutput<CustomPaletteState>;
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<unknown>;
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');
});
});

View file

@ -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);
},
};
}

View file

@ -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),
},
};
};

View file

@ -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<unknown>;
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');
});
});

View file

@ -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,
};
},
};
}

View file

@ -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<T = { [key: string]: unknown }> {
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<PaletteOutput<CustomPaletteState>>
>;

View file

@ -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';

View file

@ -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<T = { [key: string]: unknown }> {
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<CustomPaletteState>
> {
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,
};
},
};
}

View file

@ -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';

View file

@ -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%;
}
}

View file

@ -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 (
<div className="visColorPicker">
@ -140,9 +173,18 @@ export const ColorPicker = ({
/>
</legend>
</EuiScreenReaderOnly>
<EuiFlexGroup wrap={true} gutterSize="none" className="visColorPicker__value">
<EuiFlexGroup
wrap={true}
gutterSize="none"
css={visColorPickerValueStyle}
className="visColorPicker__value"
>
{legendColors.map((color) => (
<label key={color} className="visColorPicker__colorBtn">
<label
key={color}
css={visColorPickerColorBtnStyle}
className="visColorPicker__colorBtn"
>
<input
type="radio"
onChange={(e) => onChange(color, e)}
@ -155,9 +197,11 @@ export const ColorPicker = ({
type="dot"
size="l"
color={selectedColor}
className={classNames('visColorPicker__valueDot', {
'visColorPicker__valueDot-isSelected': color === selectedColor,
})}
css={[
visColorPickerValueDotStyle,
color === selectedColor ? visColorPickerValueDotSelectedStyle : null,
]}
className="visColorPicker__valueDot"
style={{ color }}
data-test-subj={`visColorPickerColor-${color}`}
/>

View file

@ -1,7 +0,0 @@
.chart-icon__subdued {
fill: $euiTextSubduedColor;
}
.chart-icon__accent {
fill: $euiColorVis0;
}

View file

@ -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 { css } from '@emotion/react';
import { useEuiTheme } from '@elastic/eui';
import { euiThemeVars } from '@kbn/ui-theme';
import { useMemo } from 'react';
export const useCommonChartStyles = () => {
const { euiTheme } = useEuiTheme();
const subdued = useMemo(
() => css`
fill: ${euiTheme.colors.subdued};
`,
[euiTheme.colors.subdued]
);
const accent = css`
fill: ${euiThemeVars.euiColorVis0};
`;
return {
chartIcon: { subdued, accent },
};
};

View file

@ -1,7 +0,0 @@
.chart__empty-placeholder {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}

View file

@ -9,8 +9,23 @@
import React from 'react';
import { EuiIcon, EuiText, IconType, EuiSpacer } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import './empty_placeholder.scss';
import classnames from 'classnames';
import { css } from '@emotion/react';
export interface EmptyPlaceholderProps {
icon: IconType;
iconColor?: string;
message?: JSX.Element;
dataTestSubj?: string;
className?: string;
}
const style = css`
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
`;
export const EmptyPlaceholder = ({
icon,
@ -18,24 +33,17 @@ export const EmptyPlaceholder = ({
message = <FormattedMessage id="charts.noDataLabel" defaultMessage="No results found" />,
dataTestSubj = 'emptyPlaceholder',
className,
}: {
icon: IconType;
iconColor?: string;
message?: JSX.Element;
dataTestSubj?: string;
className?: string;
}) => (
<>
<EuiText
data-test-subj={dataTestSubj}
className={classnames('chart__empty-placeholder', className)}
textAlign="center"
color="subdued"
size="xs"
>
<EuiIcon type={icon} color={iconColor} size="l" />
<EuiSpacer size="s" />
<p>{message}</p>
</EuiText>
</>
}: EmptyPlaceholderProps) => (
<EuiText
data-test-subj={dataTestSubj}
css={style}
className={className}
textAlign="center"
color="subdued"
size="xs"
>
<EuiIcon type={icon} color={iconColor} size="l" />
<EuiSpacer size="s" />
<p>{message}</p>
</EuiText>
);

View file

@ -6,9 +6,45 @@
* Side Public License, v 1.
*/
export { LegendToggle } from './legend_toggle';
export { ColorPicker } from './color_picker';
import React from 'react';
import { withSuspense } from '@kbn/shared-ux-utility';
export { CurrentTime } from './current_time';
export { EmptyPlaceholder } from './empty_placeholder';
import './common_chart_styles.scss';
export { useCommonChartStyles } from './common_chart_styles';
export * from './endzones';
/**
* The Lazily-loaded `ColorPicker` component. Consumers should use `React.Suspense` or
* the withSuspense` HOC to load this component.
*/
export const ColorPickerLazy = React.lazy(() =>
import('./color_picker').then(({ ColorPicker }) => ({
default: ColorPicker,
}))
);
/**
* A `ColorPicker` component that is wrapped by the `withSuspense` HOC. This component can
* be used directly by consumers and will load the `ColorPickerLazy` component lazily with
* a predefined fallback and error boundary.
*/
export const ColorPicker = withSuspense(ColorPickerLazy);
/**
* The Lazily-loaded `LegendToggle` component. Consumers should use `React.Suspense` or
* the withSuspense` HOC to load this component.
*/
export const LegendToggleLazy = React.lazy(() =>
import('./legend_toggle').then(({ LegendToggle }) => ({
default: LegendToggle,
}))
);
/**
* A `LegendToggle` component that is wrapped by the `withSuspense` HOC. This component can
* be used directly by consumers and will load the `LegendToggleLazy` component lazily with
* a predefined fallback and error boundary.
*/
export const LegendToggle = withSuspense(LegendToggleLazy);

View file

@ -1,19 +0,0 @@
.echLegend__toggle {
position: absolute;
bottom: 0;
left: 0;
z-index: 1;
margin: $euiSizeXS;
&--isOpen {
background-color: $euiColorLightestShade;
}
&--position-left,
&--position-bottom {
left: auto;
bottom: auto;
right: 0;
top: 0;
}
}

View file

@ -7,15 +7,12 @@
*/
import React, { memo, useMemo } from 'react';
import classNames from 'classnames';
import { i18n } from '@kbn/i18n';
import { htmlIdGenerator, EuiButtonIcon } from '@elastic/eui';
import { htmlIdGenerator, EuiButtonIcon, useEuiTheme } from '@elastic/eui';
import { Position } from '@elastic/charts';
import { css } from '@emotion/react';
import './legend_toggle.scss';
interface LegendToggleProps {
export interface LegendToggleProps {
onClick: () => void;
showLegend: boolean;
legendPosition: Position;
@ -23,6 +20,35 @@ interface LegendToggleProps {
const LegendToggleComponent = ({ onClick, showLegend, legendPosition }: LegendToggleProps) => {
const legendId = useMemo(() => htmlIdGenerator()('legend'), []);
const { euiTheme } = useEuiTheme();
const baseStyles = useMemo(
() => css`
position: absolute;
bottom: 0;
left: 0;
z-index: 1;
margin: ${euiTheme.size.xs};
`,
[euiTheme.size.xs]
);
const isOpenStyle = useMemo(
() => css`
background-color: ${euiTheme.colors.lightestShade};
`,
[euiTheme.colors.lightestShade]
);
const positionStyle = useMemo(
() => css`
left: auto;
bottom: auto;
right: 0;
top: 0;
`,
[]
);
return (
<EuiButtonIcon
@ -30,9 +56,11 @@ const LegendToggleComponent = ({ onClick, showLegend, legendPosition }: LegendTo
iconType="list"
color="text"
onClick={onClick}
className={classNames('echLegend__toggle', `echLegend__toggle--position-${legendPosition}`, {
'echLegend__toggle--isOpen': showLegend,
})}
css={[
baseStyles,
showLegend ? isOpenStyle : null,
['left', 'bottom'].includes(legendPosition) ? positionStyle : null,
]}
aria-label={i18n.translate('charts.legend.toggleLegendButtonAriaLabel', {
defaultMessage: 'Toggle legend',
})}