[Lens] Add search to chart switcher (#77631)

This commit is contained in:
Joe Reuter 2020-10-06 17:17:41 +02:00 committed by GitHub
parent 4d58a0041e
commit 11886bf51c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 94 additions and 31 deletions

View file

@ -16,3 +16,7 @@ img.lnsChartSwitch__chartIcon { // sass-lint:disable-line no-qualifying-elements
// The large icons aren't square so max out the width to fill the height
width: 100%;
}
.lnsChartSwitch__search {
width: 4 * $euiSizeXXL;
}

View file

@ -12,9 +12,14 @@ import {
EuiPopoverTitle,
EuiKeyPadMenu,
EuiKeyPadMenuItem,
EuiFieldSearch,
EuiFlexGroup,
EuiFlexItem,
EuiSelectableMessage,
} from '@elastic/eui';
import { flatten } from 'lodash';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { Visualization, FramePublicAPI, Datasource } from '../../../types';
import { Action } from '../state_management';
import { getSuggestions, switchToSuggestion, Suggestion } from '../suggestion_helpers';
@ -173,6 +178,8 @@ export function ChartSwitch(props: Props) {
};
}
const [searchTerm, setSearchTerm] = useState('');
const visualizationTypes = useMemo(
() =>
flyoutOpen &&
@ -184,10 +191,17 @@ export function ChartSwitch(props: Props) {
icon: t.icon,
}))
)
).map((visualizationType) => ({
...visualizationType,
selection: getSelection(visualizationType.visualizationId, visualizationType.id),
})),
)
.filter(
(visualizationType) =>
visualizationType.label.toLowerCase().includes(searchTerm.toLowerCase()) ||
(visualizationType.fullLabel &&
visualizationType.fullLabel.toLowerCase().includes(searchTerm.toLowerCase()))
)
.map((visualizationType) => ({
...visualizationType,
selection: getSelection(visualizationType.visualizationId, visualizationType.id),
})),
// eslint-disable-next-line react-hooks/exhaustive-deps
[
flyoutOpen,
@ -195,6 +209,7 @@ export function ChartSwitch(props: Props) {
props.framePublicAPI,
props.visualizationId,
props.visualizationState,
searchTerm,
]
);
@ -219,15 +234,30 @@ export function ChartSwitch(props: Props) {
anchorPosition="downLeft"
>
<EuiPopoverTitle>
{i18n.translate('xpack.lens.configPanel.selectVisualization', {
defaultMessage: 'Select a visualization',
})}
<EuiFlexGroup alignItems="center" responsive={false}>
<EuiFlexItem>
{i18n.translate('xpack.lens.configPanel.chartType', {
defaultMessage: 'Chart type',
})}
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFieldSearch
compressed
fullWidth={false}
className="lnsChartSwitch__search"
value={searchTerm}
data-test-subj="lnsChartSwitchSearch"
onChange={(e) => setSearchTerm(e.target.value)}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPopoverTitle>
<EuiKeyPadMenu>
{(visualizationTypes || []).map((v) => (
<EuiKeyPadMenuItem
key={`${v.visualizationId}:${v.id}`}
label={<span data-test-subj="visTypeTitle">{v.label}</span>}
title={v.fullLabel}
role="menuitem"
data-test-subj={`lnsChartSwitchPopover_${v.id}`}
onClick={() => commitSelection(v.selection)}
@ -251,6 +281,17 @@ export function ChartSwitch(props: Props) {
</EuiKeyPadMenuItem>
))}
</EuiKeyPadMenu>
{searchTerm && (visualizationTypes || []).length === 0 && (
<EuiSelectableMessage>
<FormattedMessage
id="xpack.lens.chartSwitch.noResults"
defaultMessage="No results found for {term}."
values={{
term: <strong>{searchTerm}</strong>,
}}
/>
</EuiSelectableMessage>
)}
</EuiPopover>
);

View file

@ -430,10 +430,26 @@ export interface FramePublicAPI {
removeLayers: (layerIds: string[]) => void;
}
/**
* A visualization type advertised to the user in the chart switcher
*/
export interface VisualizationType {
/**
* Unique id of the visualization type within the visualization defining it
*/
id: string;
/**
* Icon used in the chart switcher
*/
icon: IconType;
/**
* Visible label used in the chart switcher and above the workspace panel in collapsed state
*/
label: string;
/**
* Optional label used in chart type search if chart switcher is expanded and for tooltips
*/
fullLabel?: string;
}
export interface Visualization<T = unknown> {

View file

@ -426,6 +426,9 @@ export const visualizationTypes: VisualizationType[] = [
id: 'bar_horizontal',
icon: LensIconChartBarHorizontal,
label: i18n.translate('xpack.lens.xyVisualization.barHorizontalLabel', {
defaultMessage: 'H. Bar',
}),
fullLabel: i18n.translate('xpack.lens.xyVisualization.barHorizontalFullLabel', {
defaultMessage: 'Horizontal bar',
}),
},
@ -440,22 +443,31 @@ export const visualizationTypes: VisualizationType[] = [
id: 'bar_percentage_stacked',
icon: LensIconChartBarPercentage,
label: i18n.translate('xpack.lens.xyVisualization.stackedPercentageBarLabel', {
defaultMessage: 'Bar percentage',
defaultMessage: 'Percentage bar',
}),
},
{
id: 'bar_horizontal_stacked',
icon: LensIconChartBarHorizontalStacked,
label: i18n.translate('xpack.lens.xyVisualization.stackedBarHorizontalLabel', {
defaultMessage: 'Stacked horizontal bar',
defaultMessage: 'H. Stacked bar',
}),
fullLabel: i18n.translate('xpack.lens.xyVisualization.stackedBarHorizontalFullLabel', {
defaultMessage: 'Horizontal stacked bar',
}),
},
{
id: 'bar_horizontal_percentage_stacked',
icon: LensIconChartBarHorizontalPercentage,
label: i18n.translate('xpack.lens.xyVisualization.stackedPercentageBarHorizontalLabel', {
defaultMessage: 'Horizontal bar percentage',
defaultMessage: 'H. Percentage bar',
}),
fullLabel: i18n.translate(
'xpack.lens.xyVisualization.stackedPercentageBarHorizontalFullLabel',
{
defaultMessage: 'Horizontal percentage bar',
}
),
},
{
id: 'area',
@ -475,7 +487,7 @@ export const visualizationTypes: VisualizationType[] = [
id: 'area_percentage_stacked',
icon: LensIconChartAreaPercentage,
label: i18n.translate('xpack.lens.xyVisualization.stackedPercentageAreaLabel', {
defaultMessage: 'Area percentage',
defaultMessage: 'Percentage area',
}),
},
{

View file

@ -44,14 +44,14 @@ describe('xy_visualization', () => {
it('should show mixed xy chart when multilple series types', () => {
const desc = xyVisualization.getDescription(mixedState('bar', 'line'));
expect(desc.label).toEqual('Mixed XY chart');
expect(desc.label).toEqual('Mixed XY');
});
it('should show the preferredSeriesType if there are no layers', () => {
const desc = xyVisualization.getDescription(mixedState());
expect(desc.icon).toEqual(LensIconChartBar);
expect(desc.label).toEqual('Bar chart');
expect(desc.label).toEqual('Bar');
});
it('should show mixed horizontal bar chart when multiple horizontal bar types', () => {
@ -59,23 +59,23 @@ describe('xy_visualization', () => {
mixedState('bar_horizontal', 'bar_horizontal_stacked')
);
expect(desc.label).toEqual('Mixed horizontal bar chart');
expect(desc.label).toEqual('Mixed H. bar');
});
it('should show bar chart when bar only', () => {
const desc = xyVisualization.getDescription(mixedState('bar_horizontal', 'bar_horizontal'));
expect(desc.label).toEqual('Horizontal bar chart');
expect(desc.label).toEqual('H. Bar');
});
it('should show the chart description if not mixed', () => {
expect(xyVisualization.getDescription(mixedState('area')).label).toEqual('Area chart');
expect(xyVisualization.getDescription(mixedState('line')).label).toEqual('Line chart');
expect(xyVisualization.getDescription(mixedState('area')).label).toEqual('Area');
expect(xyVisualization.getDescription(mixedState('line')).label).toEqual('Line');
expect(xyVisualization.getDescription(mixedState('area_stacked')).label).toEqual(
'Stacked area chart'
'Stacked area'
);
expect(xyVisualization.getDescription(mixedState('bar_horizontal_stacked')).label).toEqual(
'Stacked horizontal bar chart'
'H. Stacked bar'
);
});
});

View file

@ -53,7 +53,7 @@ function getDescription(state?: State) {
return {
icon: LensIconChartBarHorizontal,
label: i18n.translate('xpack.lens.xyVisualization.mixedBarHorizontalLabel', {
defaultMessage: 'Mixed horizontal bar',
defaultMessage: 'Mixed H. bar',
}),
};
}
@ -118,14 +118,10 @@ export const xyVisualization: Visualization<State> = {
getDescription(state) {
const { icon, label } = getDescription(state);
const chartLabel = i18n.translate('xpack.lens.xyVisualization.chartLabel', {
defaultMessage: '{label} chart',
values: { label },
});
return {
icon: icon || defaultIcon,
label: chartLabel,
label,
};
},

View file

@ -9623,15 +9623,12 @@
"xpack.lens.xySuggestions.unstackedChartTitle": "スタックが解除されました",
"xpack.lens.xySuggestions.yAxixConjunctionSign": " と ",
"xpack.lens.xyVisualization.areaLabel": "エリア",
"xpack.lens.xyVisualization.barHorizontalLabel": "横棒",
"xpack.lens.xyVisualization.barLabel": "バー",
"xpack.lens.xyVisualization.chartLabel": "{label} チャート",
"xpack.lens.xyVisualization.lineLabel": "折れ線",
"xpack.lens.xyVisualization.mixedBarHorizontalLabel": "ミックスされた横棒",
"xpack.lens.xyVisualization.mixedLabel": "ミックスされた XY",
"xpack.lens.xyVisualization.noDataLabel": "結果が見つかりませんでした",
"xpack.lens.xyVisualization.stackedAreaLabel": "スタックされたエリア",
"xpack.lens.xyVisualization.stackedBarHorizontalLabel": "スタックされた横棒",
"xpack.lens.xyVisualization.stackedBarLabel": "スタックされたバー",
"xpack.lens.xyVisualization.xyLabel": "XY",
"xpack.licenseMgmt.app.checkingPermissionsErrorMessage": "パーミッションの確認中にエラーが発生",

View file

@ -9629,15 +9629,12 @@
"xpack.lens.xySuggestions.unstackedChartTitle": "非堆叠",
"xpack.lens.xySuggestions.yAxixConjunctionSign": " &amp; ",
"xpack.lens.xyVisualization.areaLabel": "面积图",
"xpack.lens.xyVisualization.barHorizontalLabel": "水平条形图",
"xpack.lens.xyVisualization.barLabel": "条形图",
"xpack.lens.xyVisualization.chartLabel": "{label} 图表",
"xpack.lens.xyVisualization.lineLabel": "折线图",
"xpack.lens.xyVisualization.mixedBarHorizontalLabel": "混合水平条形图",
"xpack.lens.xyVisualization.mixedLabel": "混合 XY",
"xpack.lens.xyVisualization.noDataLabel": "找不到结果",
"xpack.lens.xyVisualization.stackedAreaLabel": "堆叠面积图",
"xpack.lens.xyVisualization.stackedBarHorizontalLabel": "堆叠水平条形图",
"xpack.lens.xyVisualization.stackedBarLabel": "堆叠条形图",
"xpack.lens.xyVisualization.xyLabel": "XY",
"xpack.licenseMgmt.app.checkingPermissionsErrorMessage": "检查权限时出错",