mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Maps] clean up icon category UI (#61116)
* [Maps] clean up icon category UI * fix jest tests * add unit test for getFirstUnusedSymbol * remove duplicate icon stop values Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
aa73e2aee3
commit
29a3f55985
9 changed files with 128 additions and 41 deletions
|
@ -120,7 +120,7 @@ describe('migrateSymbolStyleDescriptor', () => {
|
|||
},
|
||||
icon: {
|
||||
type: STYLE_TYPE.STATIC,
|
||||
options: { value: 'airfield' },
|
||||
options: { value: 'marker' },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -4,15 +4,5 @@
|
|||
& + & {
|
||||
margin-top: $euiSizeS;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
.mapColorStop__icons {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
display: block;
|
||||
animation: mapColorStopBecomeVisible $euiAnimSpeedFast $euiAnimSlightResistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import { EuiButtonIcon, EuiColorPicker, EuiFlexGroup, EuiFlexItem, EuiFormRow }
|
|||
|
||||
function getColorStopRow({ index, errors, stopInput, onColorChange, color, deleteButton, onAdd }) {
|
||||
const colorPickerButtons = (
|
||||
<div className="mapColorStop__icons">
|
||||
<div>
|
||||
{deleteButton}
|
||||
<EuiButtonIcon
|
||||
iconType="plusInCircle"
|
||||
|
|
|
@ -70,6 +70,7 @@ export class IconSelect extends Component {
|
|||
fullWidth
|
||||
compressed
|
||||
onKeyDown={this._handleKeyboardActivity}
|
||||
append={this.props.append}
|
||||
>
|
||||
<EuiFieldText
|
||||
onClick={this._togglePopover}
|
||||
|
|
|
@ -11,6 +11,7 @@ import { getOtherCategoryLabel } from '../../style_util';
|
|||
import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiFieldText } from '@elastic/eui';
|
||||
import { IconSelect } from './icon_select';
|
||||
import { StopInput } from '../stop_input';
|
||||
import { PREFERRED_ICONS } from '../../symbol_utils';
|
||||
|
||||
function isDuplicateStop(targetStop, iconStops) {
|
||||
const stops = iconStops.filter(({ stop }) => {
|
||||
|
@ -19,9 +20,31 @@ function isDuplicateStop(targetStop, iconStops) {
|
|||
return stops.length > 1;
|
||||
}
|
||||
|
||||
export function getFirstUnusedSymbol(symbolOptions, iconStops) {
|
||||
const firstUnusedPreferredIconId = PREFERRED_ICONS.find(iconId => {
|
||||
const isSymbolBeingUsed = iconStops.some(({ icon }) => {
|
||||
return icon === iconId;
|
||||
});
|
||||
return !isSymbolBeingUsed;
|
||||
});
|
||||
|
||||
if (firstUnusedPreferredIconId) {
|
||||
return firstUnusedPreferredIconId;
|
||||
}
|
||||
|
||||
const firstUnusedSymbol = symbolOptions.find(({ value }) => {
|
||||
const isSymbolBeingUsed = iconStops.some(({ icon }) => {
|
||||
return icon === value;
|
||||
});
|
||||
return !isSymbolBeingUsed;
|
||||
});
|
||||
|
||||
return firstUnusedSymbol ? firstUnusedSymbol.value : DEFAULT_ICON;
|
||||
}
|
||||
|
||||
const DEFAULT_ICON_STOPS = [
|
||||
{ stop: null, icon: DEFAULT_ICON }, //first stop is the "other" color
|
||||
{ stop: '', icon: DEFAULT_ICON },
|
||||
{ stop: null, icon: PREFERRED_ICONS[0] }, //first stop is the "other" color
|
||||
{ stop: '', icon: PREFERRED_ICONS[1] },
|
||||
];
|
||||
|
||||
export function IconStops({
|
||||
|
@ -58,7 +81,7 @@ export function IconStops({
|
|||
...iconStops.slice(0, index + 1),
|
||||
{
|
||||
stop: '',
|
||||
icon: DEFAULT_ICON,
|
||||
icon: getFirstUnusedSymbol(symbolOptions, iconStops),
|
||||
},
|
||||
...iconStops.slice(index + 1),
|
||||
],
|
||||
|
@ -66,12 +89,12 @@ export function IconStops({
|
|||
};
|
||||
const onRemove = () => {
|
||||
onChange({
|
||||
iconStops: [...iconStops.slice(0, index), ...iconStops.slice(index + 1)],
|
||||
customMapStops: [...iconStops.slice(0, index), ...iconStops.slice(index + 1)],
|
||||
});
|
||||
};
|
||||
|
||||
let deleteButton;
|
||||
if (index > 0) {
|
||||
if (iconStops.length > 2 && index !== 0) {
|
||||
deleteButton = (
|
||||
<EuiButtonIcon
|
||||
iconType="trash"
|
||||
|
@ -87,6 +110,19 @@ export function IconStops({
|
|||
);
|
||||
}
|
||||
|
||||
const iconStopButtons = (
|
||||
<div>
|
||||
{deleteButton}
|
||||
<EuiButtonIcon
|
||||
iconType="plusInCircle"
|
||||
color="primary"
|
||||
aria-label="Add"
|
||||
title="Add"
|
||||
onClick={onAdd}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
const errors = [];
|
||||
// TODO check for duplicate values and add error messages here
|
||||
|
||||
|
@ -116,29 +152,20 @@ export function IconStops({
|
|||
error={errors}
|
||||
display="rowCompressed"
|
||||
>
|
||||
<div>
|
||||
<EuiFlexGroup responsive={false} alignItems="center" gutterSize="xs">
|
||||
<EuiFlexItem>{stopInput}</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<IconSelect
|
||||
isDarkMode={isDarkMode}
|
||||
onChange={onIconSelect}
|
||||
symbolOptions={symbolOptions}
|
||||
value={icon}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<div className="mapColorStop__icons">
|
||||
{deleteButton}
|
||||
<EuiButtonIcon
|
||||
iconType="plusInCircle"
|
||||
color="primary"
|
||||
aria-label="Add"
|
||||
title="Add"
|
||||
onClick={onAdd}
|
||||
<EuiFlexGroup alignItems="center" gutterSize="xs">
|
||||
<EuiFlexItem grow={false} className="mapStyleSettings__fixedBox">
|
||||
{stopInput}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<IconSelect
|
||||
isDarkMode={isDarkMode}
|
||||
onChange={onIconSelect}
|
||||
symbolOptions={symbolOptions}
|
||||
value={icon}
|
||||
append={iconStopButtons}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFormRow>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { getFirstUnusedSymbol } from './icon_stops';
|
||||
|
||||
describe('getFirstUnusedSymbol', () => {
|
||||
const symbolOptions = [{ value: 'icon1' }, { value: 'icon2' }];
|
||||
|
||||
test('Should return first unused icon from PREFERRED_ICONS', () => {
|
||||
const iconStops = [
|
||||
{ stop: 'category1', icon: 'circle' },
|
||||
{ stop: 'category2', icon: 'marker' },
|
||||
];
|
||||
const nextIcon = getFirstUnusedSymbol(symbolOptions, iconStops);
|
||||
expect(nextIcon).toBe('square');
|
||||
});
|
||||
|
||||
test('Should fallback to first unused general icons when all PREFERRED_ICONS are used', () => {
|
||||
const iconStops = [
|
||||
{ stop: 'category1', icon: 'circle' },
|
||||
{ stop: 'category2', icon: 'marker' },
|
||||
{ stop: 'category3', icon: 'square' },
|
||||
{ stop: 'category4', icon: 'star' },
|
||||
{ stop: 'category5', icon: 'triangle' },
|
||||
{ stop: 'category6', icon: 'hospital' },
|
||||
{ stop: 'category7', icon: 'circle-stroked' },
|
||||
{ stop: 'category8', icon: 'marker-stroked' },
|
||||
{ stop: 'category9', icon: 'square-stroked' },
|
||||
{ stop: 'category10', icon: 'star-stroked' },
|
||||
{ stop: 'category11', icon: 'triangle-stroked' },
|
||||
{ stop: 'category12', icon: 'icon1' },
|
||||
];
|
||||
const nextIcon = getFirstUnusedSymbol(symbolOptions, iconStops);
|
||||
expect(nextIcon).toBe('icon2');
|
||||
});
|
||||
|
||||
test('Should fallback to default icon when all icons are used', () => {
|
||||
const iconStops = [
|
||||
{ stop: 'category1', icon: 'circle' },
|
||||
{ stop: 'category2', icon: 'marker' },
|
||||
{ stop: 'category3', icon: 'square' },
|
||||
{ stop: 'category4', icon: 'star' },
|
||||
{ stop: 'category5', icon: 'triangle' },
|
||||
{ stop: 'category6', icon: 'hospital' },
|
||||
{ stop: 'category7', icon: 'circle-stroked' },
|
||||
{ stop: 'category8', icon: 'marker-stroked' },
|
||||
{ stop: 'category9', icon: 'square-stroked' },
|
||||
{ stop: 'category10', icon: 'star-stroked' },
|
||||
{ stop: 'category11', icon: 'triangle-stroked' },
|
||||
{ stop: 'category12', icon: 'icon1' },
|
||||
{ stop: 'category13', icon: 'icon2' },
|
||||
];
|
||||
const nextIcon = getFirstUnusedSymbol(symbolOptions, iconStops);
|
||||
expect(nextIcon).toBe('marker');
|
||||
});
|
||||
});
|
|
@ -101,6 +101,16 @@ const ICON_PALETTES = [
|
|||
},
|
||||
];
|
||||
|
||||
// PREFERRED_ICONS is used to provide less random default icon values for forms that need default icon values
|
||||
export const PREFERRED_ICONS = [];
|
||||
ICON_PALETTES.forEach(iconPalette => {
|
||||
iconPalette.icons.forEach(iconId => {
|
||||
if (!PREFERRED_ICONS.includes(iconId)) {
|
||||
PREFERRED_ICONS.push(iconId);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
export function getIconPaletteOptions(isDarkMode) {
|
||||
return ICON_PALETTES.map(({ id, icons }) => {
|
||||
const iconsDisplay = icons.map(iconId => {
|
||||
|
|
|
@ -96,7 +96,7 @@ describe('getDescriptorWithMissingStylePropsRemoved', () => {
|
|||
},
|
||||
icon: {
|
||||
options: {
|
||||
value: 'airfield',
|
||||
value: 'marker',
|
||||
},
|
||||
type: 'STATIC',
|
||||
},
|
||||
|
|
|
@ -188,7 +188,7 @@ export enum LABEL_BORDER_SIZES {
|
|||
LARGE = 'LARGE',
|
||||
}
|
||||
|
||||
export const DEFAULT_ICON = 'airfield';
|
||||
export const DEFAULT_ICON = 'marker';
|
||||
|
||||
export enum VECTOR_STYLES {
|
||||
SYMBOLIZE_AS = 'symbolizeAs',
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue