Use EuiTokens for ES field types (#57911) (#58293)

* [FieldIcon] Refactor to extend EuiToken and props

* [Add to FieldIcon] Export props

* [Graph] Updated instances of FieldIcon

* [Maps] Update FieldIcon instance

* [Lens] Update FieldIcon usage


* [Discover] Update FieldIcon usage


* Remove comment

* [Lens] Delete unused files

* [Graph] Fix alignments

* More cleanup

* Fixing snaps

* [Lens] Removing method `getColorForDataType`

The method no longer returns the correct color and EuiProgress doesn’t currently support all the colors from the token map. @cchaos will follow up with a PR to address the coloring of the field data charts. Right now they will fallback to pink for progress and default for charts.

* [Maps] Fixing implementations of FieldIcon

* [Graph] Using css utility class instead of custom class

* Snap

* Fix css to use var
This commit is contained in:
Caroline Horn 2020-02-22 17:23:14 -05:00 committed by GitHub
parent 31bc4114b1
commit ee5ebd963a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 426 additions and 339 deletions

View file

@ -206,7 +206,7 @@ discover-app {
background-color: transparent;
}
.dscField--noResults {
.dscFieldName--noResults {
color: $euiColorDarkShade;
}

View file

@ -1,73 +1,109 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`FieldName renders a geo field, useShortDots is set to true 1`] = `
<span
class="dscField--noResults"
title="Geo point field"
<div
class="euiFlexGroup euiFlexGroup--gutterSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow dscFieldName dscFieldName--noResults"
>
<svg
aria-hidden="true"
aria-label="Geo point field"
class="euiIcon euiIcon--small euiIcon-isLoading"
focusable="false"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
/>
<span
class="dscFieldName"
<div
class="euiFlexItem euiFlexItem--flexGrowZero"
>
t.t.test
</span>
</span>
<span
aria-label="Geo point field"
class="euiToken euiToken--euiColorVis5 euiToken--square euiToken--light euiToken--small kbnFieldIcon"
title="Geo point field"
>
<svg
aria-hidden="true"
class="euiIcon euiIcon--medium euiIcon-isLoading"
focusable="false"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
/>
</span>
</div>
<div
class="euiFlexItem eui-textTruncate"
>
<span
class="dscFieldName__displayName eui-textTruncate"
>
t.t.test
</span>
</div>
</div>
`;
exports[`FieldName renders a number field by providing a field record, useShortDots is set to false 1`] = `
<span
class=""
title="Number field"
<div
class="euiFlexGroup euiFlexGroup--gutterSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow dscFieldName"
>
<svg
aria-hidden="true"
aria-label="Number field"
class="euiIcon euiIcon--small euiIcon-isLoading"
focusable="false"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
/>
<span
class="dscFieldName"
<div
class="euiFlexItem euiFlexItem--flexGrowZero"
>
test.test.test
</span>
</span>
<span
aria-label="Number field"
class="euiToken euiToken--euiColorVis0 euiToken--square euiToken--light euiToken--small kbnFieldIcon"
title="Number field"
>
<svg
aria-hidden="true"
class="euiIcon euiIcon--medium euiIcon-isLoading"
focusable="false"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
/>
</span>
</div>
<div
class="euiFlexItem eui-textTruncate"
>
<span
class="dscFieldName__displayName eui-textTruncate"
>
test.test.test
</span>
</div>
</div>
`;
exports[`FieldName renders a string field by providing fieldType and fieldName 1`] = `
<span
class=""
title="String field"
<div
class="euiFlexGroup euiFlexGroup--gutterSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow dscFieldName"
>
<svg
aria-hidden="true"
aria-label="String field"
class="euiIcon euiIcon--small euiIcon-isLoading"
focusable="false"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
/>
<span
class="dscFieldName"
<div
class="euiFlexItem euiFlexItem--flexGrowZero"
>
test
</span>
</span>
<span
aria-label="String field"
class="euiToken euiToken--euiColorVis1 euiToken--square euiToken--light euiToken--small kbnFieldIcon"
title="String field"
>
<svg
aria-hidden="true"
class="euiIcon euiIcon--medium euiIcon-isLoading"
focusable="false"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
/>
</span>
</div>
<div
class="euiFlexItem eui-textTruncate"
>
<span
class="dscFieldName__displayName eui-textTruncate"
>
test
</span>
</div>
</div>
`;

View file

@ -18,7 +18,9 @@
*/
import React from 'react';
import classNames from 'classnames';
import { FieldIcon } from '../../../../../../../../../plugins/kibana_react/public';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { FieldIcon, FieldIconProps } from '../../../../../../../../../plugins/kibana_react/public';
import { shortenDottedString } from '../../../../../../../../../plugins/data/common/utils';
import { getFieldTypeName } from './field_type_name';
@ -35,25 +37,35 @@ interface Props {
fieldName?: string;
fieldType?: string;
useShortDots?: boolean;
fieldIconProps?: Omit<FieldIconProps, 'type'>;
}
export function FieldName({ field, fieldName, fieldType, useShortDots }: Props) {
export function FieldName({ field, fieldName, fieldType, useShortDots, fieldIconProps }: Props) {
const type = field ? String(field.type) : String(fieldType);
const typeName = getFieldTypeName(type);
const name = field ? String(field.name) : String(fieldName);
const displayName = useShortDots ? shortenDottedString(name) : name;
const className = classNames({
'dscField--noResults': field ? !field.rowCount && !field.scripted : false,
// this is currently not styled, should display an icon
scripted: field ? field.scripted : false,
const noResults = field ? !field.rowCount && !field.scripted : false;
const className = classNames('dscFieldName', {
'dscFieldName--noResults': noResults,
});
return (
<span className={className} title={typeName}>
<FieldIcon type={type} label={typeName} />
<span className="dscFieldName">{displayName}</span>
</span>
<EuiFlexGroup className={className} alignItems="center" gutterSize="s" responsive={false}>
<EuiFlexItem grow={false}>
<FieldIcon
type={type}
label={typeName}
scripted={field ? field.scripted : false}
{...fieldIconProps}
/>
</EuiFlexItem>
<EuiFlexItem className="eui-textTruncate">
<span className="dscFieldName__displayName eui-textTruncate">{displayName}</span>
</EuiFlexItem>
</EuiFlexGroup>
);
}

View file

@ -10,7 +10,6 @@
.dscFieldName {
color: $euiColorDarkShade;
padding-left: $euiSizeS;
}

View file

@ -87,7 +87,12 @@ export function DocViewTableRow({
</td>
)}
<td className="kbnDocViewer__field">
<FieldName field={fieldMapping} fieldName={field} fieldType={fieldType} />
<FieldName
field={fieldMapping}
fieldName={field}
fieldType={fieldType}
fieldIconProps={{ fill: 'none', color: 'gray' }}
/>
</td>
<td>
{isCollapsible && (

View file

@ -1,37 +1,159 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`FieldIcon renders a blackwhite icon for a string 1`] = `
<EuiIcon
aria-label="string"
size="s"
type="string"
/>
`;
exports[`FieldIcon renders a colored icon for a number 1`] = `
<EuiIcon
exports[`FieldIcon changes fill when scripted is true 1`] = `
<EuiToken
aria-label="test"
color="#54B399"
className="kbnFieldIcon"
fill="dark"
iconType="tokenNumber"
size="s"
type="number"
title="test"
/>
`;
exports[`FieldIcon renders an icon for an unknown type 1`] = `
<EuiIcon
<EuiToken
aria-label="test"
color="#54B399"
className="kbnFieldIcon"
color="gray"
iconType="questionInCircle"
size="s"
type="questionInCircle"
title="test"
/>
`;
exports[`FieldIcon renders known field types _source is rendered 1`] = `
<EuiToken
aria-label="_source"
className="kbnFieldIcon"
color="gray"
iconType="editorCodeBlock"
size="s"
title="_source"
/>
`;
exports[`FieldIcon renders known field types boolean is rendered 1`] = `
<EuiToken
aria-label="boolean"
className="kbnFieldIcon"
iconType="tokenBoolean"
size="s"
title="boolean"
/>
`;
exports[`FieldIcon renders known field types conflict is rendered 1`] = `
<EuiToken
aria-label="conflict"
className="kbnFieldIcon"
color="euiVisColor9"
iconType="alert"
size="s"
title="conflict"
/>
`;
exports[`FieldIcon renders known field types date is rendered 1`] = `
<EuiToken
aria-label="date"
className="kbnFieldIcon"
iconType="tokenDate"
size="s"
title="date"
/>
`;
exports[`FieldIcon renders known field types geo_point is rendered 1`] = `
<EuiToken
aria-label="geo_point"
className="kbnFieldIcon"
iconType="tokenGeo"
size="s"
title="geo_point"
/>
`;
exports[`FieldIcon renders known field types geo_shape is rendered 1`] = `
<EuiToken
aria-label="geo_shape"
className="kbnFieldIcon"
iconType="tokenGeo"
size="s"
title="geo_shape"
/>
`;
exports[`FieldIcon renders known field types ip is rendered 1`] = `
<EuiToken
aria-label="ip"
className="kbnFieldIcon"
iconType="tokenIP"
size="s"
title="ip"
/>
`;
exports[`FieldIcon renders known field types murmur3 is rendered 1`] = `
<EuiToken
aria-label="murmur3"
className="kbnFieldIcon"
iconType="tokenFile"
size="s"
title="murmur3"
/>
`;
exports[`FieldIcon renders known field types nested is rendered 1`] = `
<EuiToken
aria-label="nested"
className="kbnFieldIcon"
iconType="tokenNested"
size="s"
title="nested"
/>
`;
exports[`FieldIcon renders known field types number is rendered 1`] = `
<EuiToken
aria-label="number"
className="kbnFieldIcon"
iconType="tokenNumber"
size="s"
title="number"
/>
`;
exports[`FieldIcon renders known field types string is rendered 1`] = `
<EuiToken
aria-label="string"
className="kbnFieldIcon"
iconType="tokenString"
size="s"
title="string"
/>
`;
exports[`FieldIcon renders with className if provided 1`] = `
<EuiIcon
<EuiToken
aria-label="test"
className="myClass"
color="#54B399"
className="kbnFieldIcon myClass"
color="gray"
iconType="questionInCircle"
size="s"
type="questionInCircle"
title="test"
/>
`;
exports[`FieldIcon supports same props as EuiToken 1`] = `
<EuiToken
aria-label="test"
className="kbnFieldIcon"
color="euiColorVis0"
fill="none"
iconType="tokenNumber"
shape="circle"
size="l"
title="test"
/>
`;

View file

@ -18,24 +18,44 @@
*/
import React from 'react';
import { shallow } from 'enzyme';
import { FieldIcon } from './field_icon';
import { FieldIcon, typeToEuiIconMap } from './field_icon';
test('FieldIcon renders a blackwhite icon for a string', () => {
const component = shallow(<FieldIcon type="string" />);
expect(component).toMatchSnapshot();
});
const availableTypes = Object.keys(typeToEuiIconMap);
test('FieldIcon renders a colored icon for a number', () => {
const component = shallow(<FieldIcon type="number" label="test" useColor />);
expect(component).toMatchSnapshot();
describe('FieldIcon renders known field types', () => {
availableTypes.forEach(type => {
test(`${type} is rendered`, () => {
const component = shallow(<FieldIcon type={type} />);
expect(component).toMatchSnapshot();
});
});
});
test('FieldIcon renders an icon for an unknown type', () => {
const component = shallow(<FieldIcon type="sdfsdf" label="test" useColor />);
const component = shallow(<FieldIcon type="sdfsdf" label="test" />);
expect(component).toMatchSnapshot();
});
test('FieldIcon supports same props as EuiToken', () => {
const component = shallow(
<FieldIcon
type="number"
label="test"
color="euiColorVis0"
size="l"
shape="circle"
fill="none"
/>
);
expect(component).toMatchSnapshot();
});
test('FieldIcon changes fill when scripted is true', () => {
const component = shallow(<FieldIcon type="number" label="test" scripted={true} />);
expect(component).toMatchSnapshot();
});
test('FieldIcon renders with className if provided', () => {
const component = shallow(<FieldIcon type="sdfsdf" label="test" className="myClass" useColor />);
const component = shallow(<FieldIcon type="sdfsdf" label="test" className="myClass" />);
expect(component).toMatchSnapshot();
});

View file

@ -17,14 +17,10 @@
* under the License.
*/
import React from 'react';
import { euiPaletteColorBlind, EuiIcon } from '@elastic/eui';
import { IconSize } from '@elastic/eui/src/components/icon/icon';
import classNames from 'classnames';
import { EuiToken, EuiTokenProps } from '@elastic/eui';
interface IconMapEntry {
icon: string;
color: string;
}
interface FieldIconProps {
export interface FieldIconProps extends Omit<EuiTokenProps, 'iconType'> {
type:
| 'boolean'
| 'conflict'
@ -39,51 +35,50 @@ interface FieldIconProps {
| string
| 'nested';
label?: string;
size?: IconSize;
useColor?: boolean;
className?: string;
scripted?: boolean;
}
const colors = euiPaletteColorBlind();
// defaultIcon => a unknown datatype
const defaultIcon = { icon: 'questionInCircle', color: colors[0] };
const defaultIcon = { iconType: 'questionInCircle', color: 'gray' };
export const typeToEuiIconMap: Partial<Record<string, IconMapEntry>> = {
boolean: { icon: 'invert', color: colors[5] },
export const typeToEuiIconMap: Partial<Record<string, EuiTokenProps>> = {
boolean: { iconType: 'tokenBoolean' },
// icon for an index pattern mapping conflict in discover
conflict: { icon: 'alert', color: colors[8] },
date: { icon: 'calendar', color: colors[7] },
geo_point: { icon: 'globe', color: colors[2] },
geo_shape: { icon: 'globe', color: colors[2] },
ip: { icon: 'storage', color: colors[8] },
conflict: { iconType: 'alert', color: 'euiVisColor9' },
date: { iconType: 'tokenDate' },
geo_point: { iconType: 'tokenGeo' },
geo_shape: { iconType: 'tokenGeo' },
ip: { iconType: 'tokenIP' },
// is a plugin's data type https://www.elastic.co/guide/en/elasticsearch/plugins/current/mapper-murmur3-usage.html
murmur3: { icon: 'document', color: colors[1] },
number: { icon: 'number', color: colors[0] },
_source: { icon: 'editorCodeBlock', color: colors[3] },
string: { icon: 'string', color: colors[4] },
nested: { icon: 'nested', color: colors[2] },
murmur3: { iconType: 'tokenFile' },
number: { iconType: 'tokenNumber' },
_source: { iconType: 'editorCodeBlock', color: 'gray' },
string: { iconType: 'tokenString' },
nested: { iconType: 'tokenNested' },
};
/**
* Field icon used across the app
* Field token icon used across the app
*/
export function FieldIcon({
type,
label,
size = 's',
useColor = false,
className = undefined,
scripted,
className,
...rest
}: FieldIconProps) {
const euiIcon = typeToEuiIconMap[type] || defaultIcon;
const token = typeToEuiIconMap[type] || defaultIcon;
return (
<EuiIcon
type={euiIcon.icon}
<EuiToken
{...token}
className={classNames('kbnFieldIcon', className)}
aria-label={label || type}
size={size as IconSize}
color={useColor || type === 'conflict' ? euiIcon.color : undefined}
className={className}
title={label || type}
size={size as EuiTokenProps['size']}
fill={scripted ? 'dark' : undefined}
{...rest}
/>
);
}

View file

@ -237,10 +237,18 @@ export function FieldEditor({
renderOption={(option, searchValue, contentClassName) => {
const { type, label } = option;
return (
<span className={contentClassName}>
<FieldIcon type={type!} size="m" useColor />{' '}
<EuiHighlight search={searchValue}>{label}</EuiHighlight>
</span>
<EuiFlexGroup
className={contentClassName}
gutterSize="s"
alignItems="center"
>
<EuiFlexItem grow={null}>
<FieldIcon type={type!} fill="none" />
</EuiFlexItem>
<EuiFlexItem>
<EuiHighlight search={searchValue}>{label}</EuiHighlight>
</EuiFlexItem>
</EuiFlexGroup>
);
}}
compressed

View file

@ -1,37 +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;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import { ICON_TYPES, euiPaletteColorBlind, EuiIcon } from '@elastic/eui';
function stringToNum(s: string) {
return Array.from(s).reduce((acc, ch) => acc + ch.charCodeAt(0), 1);
}
function getIconForDataType(dataType: string) {
const icons: Partial<Record<string, UnwrapArray<typeof ICON_TYPES>>> = {
boolean: 'invert',
date: 'calendar',
geo_point: 'globe',
ip: 'storage',
};
return icons[dataType] || ICON_TYPES.find(t => t === dataType) || 'document';
}
export function getColorForDataType(type: string) {
const iconType = getIconForDataType(type);
const colors = euiPaletteColorBlind();
const colorIndex = stringToNum(iconType) % colors.length;
return colors[colorIndex];
}
export type UnwrapArray<T> = T extends Array<infer P> ? P : T;
export function FieldIcon({ type }: { type: string }) {
const iconType = getIconForDataType(type);
return <EuiIcon type={iconType} color={getColorForDataType(type)} />;
}

View file

@ -122,7 +122,7 @@ function toOptions(
.filter(field => isExplorable(field) || field.selected)
.map(field => ({
label: field.name,
prepend: <FieldIcon type={field.type} size="m" useColor />,
prepend: <FieldIcon className="eui-alignMiddle" type={field.type} fill="none" />,
checked: field.selected ? 'on' : undefined,
}))
);

View file

@ -1,10 +1,16 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`LensFieldIcon accepts FieldIcon props 1`] = `
<FieldIcon
className="lnsFieldListPanel__fieldIcon"
fill="none"
type="date"
/>
`;
exports[`LensFieldIcon renders properly 1`] = `
<FieldIcon
className="lnsFieldListPanel__fieldIcon"
size="m"
type="date"
useColor={true}
/>
`;

View file

@ -52,3 +52,16 @@
@include euiFormControlLayoutPadding(1, 'right');
@include euiFormControlLayoutPadding(1, 'left');
}
.lnsInnerIndexPatternDataPanel__filterType {
padding: $euiSizeS;
}
.lnsInnerIndexPatternDataPanel__filterTypeInner {
display: flex;
align-items: center;
.lnsFieldListPanel__fieldIcon {
margin-right: $euiSizeS;
}
}

View file

@ -14,7 +14,7 @@
}
.lnsFieldItem--missing {
background: lightOrDarkTheme(transparentize($euiColorMediumShade, .9), $euiColorEmptyShade);
background: lightOrDarkTheme(transparentize($euiColorMediumShade, 0.9), $euiColorEmptyShade);
color: $euiColorDarkShade;
}
@ -24,10 +24,10 @@
display: flex;
align-items: flex-start;
transition: box-shadow $euiAnimSpeedFast $euiAnimSlightResistance,
background-color $euiAnimSpeedFast $euiAnimSlightResistance; // sass-lint:disable-line indentation
background-color $euiAnimSpeedFast $euiAnimSlightResistance; // sass-lint:disable-line indentation
.lnsFieldItem__name {
margin-left: $euiSizeXS;
margin-left: $euiSizeS;
flex-grow: 1;
}
@ -37,7 +37,8 @@
}
.lnsFieldListPanel__fieldIcon {
margin-top: 2px;
margin-top: $euiSizeXS / 2;
margin-right: $euiSizeXS / 2;
}
.lnsFieldItem__infoIcon {

View file

@ -384,6 +384,7 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({
data-test-subj="lnsIndexPatternTypeFilterOptions"
items={(availableFieldTypes as DataType[]).map(type => (
<EuiContextMenuItem
className="lnsInnerIndexPatternDataPanel__filterType"
key={type}
icon={localState.typeFilter.includes(type) ? 'check' : 'empty'}
data-test-subj={`typeFilter-${type}`}
@ -397,7 +398,9 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({
}));
}}
>
<LensFieldIcon type={type} /> {fieldTypeNames[type]}
<span className="lnsInnerIndexPatternDataPanel__filterTypeInner">
<LensFieldIcon type={type} /> {fieldTypeNames[type]}
</span>
</EuiContextMenuItem>
))}
/>

View file

@ -169,6 +169,7 @@ export function FieldSelect({
<EuiFlexItem grow={null}>
<LensFieldIcon
type={((option.value as unknown) as { dataType: DataType }).dataType}
fill="none"
/>
</EuiFlexItem>
<EuiFlexItem>

View file

@ -1,55 +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;
* you may not use this file except in compliance with the Elastic License.
*/
/*
* 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 { shallow } from 'enzyme';
import React from 'react';
import { FieldIcon } from './field_icon';
describe('FieldIcon', () => {
it('should render icons', () => {
expect(shallow(<FieldIcon type="boolean" />)).toMatchInlineSnapshot(`
<EuiIcon
className="lnsFieldListPanel__fieldIcon lnsFieldListPanel__fieldIcon--boolean"
color="#D6BF57"
type="invert"
/>
`);
expect(shallow(<FieldIcon type="date" />)).toMatchInlineSnapshot(`
<EuiIcon
className="lnsFieldListPanel__fieldIcon lnsFieldListPanel__fieldIcon--date"
color="#DA8B45"
type="calendar"
/>
`);
expect(shallow(<FieldIcon type="number" />)).toMatchInlineSnapshot(`
<EuiIcon
className="lnsFieldListPanel__fieldIcon lnsFieldListPanel__fieldIcon--number"
color="#54B399"
type="number"
/>
`);
expect(shallow(<FieldIcon type="string" />)).toMatchInlineSnapshot(`
<EuiIcon
className="lnsFieldListPanel__fieldIcon lnsFieldListPanel__fieldIcon--string"
color="#CA8EAE"
type="string"
/>
`);
expect(shallow(<FieldIcon type="ip" />)).toMatchInlineSnapshot(`
<EuiIcon
className="lnsFieldListPanel__fieldIcon lnsFieldListPanel__fieldIcon--ip"
color="#AA6556"
type="ip"
/>
`);
});
});

View file

@ -1,43 +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;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import { ICON_TYPES, euiPaletteColorBlind, EuiIcon } from '@elastic/eui';
import classNames from 'classnames';
import { DataType } from '../types';
function stringToNum(s: string) {
return Array.from(s).reduce((acc, ch) => acc + ch.charCodeAt(0), 1);
}
function getIconForDataType(dataType: string) {
const icons: Partial<Record<string, UnwrapArray<typeof ICON_TYPES>>> = {
boolean: 'invert',
date: 'calendar',
ip: 'ip',
};
return icons[dataType] || ICON_TYPES.find(t => t === dataType) || 'empty';
}
export function getColorForDataType(type: string) {
const iconType = getIconForDataType(type);
const colors = euiPaletteColorBlind();
const colorIndex = stringToNum(iconType) % colors.length;
return colors[colorIndex];
}
export type UnwrapArray<T> = T extends Array<infer P> ? P : T;
export function FieldIcon({ type }: { type: DataType }) {
const iconType = getIconForDataType(type);
const classes = classNames(
'lnsFieldListPanel__fieldIcon',
`lnsFieldListPanel__fieldIcon--${type}`
);
return <EuiIcon type={iconType} color={getColorForDataType(type)} className={classes} />;
}

View file

@ -46,7 +46,7 @@ import { DragDrop } from '../drag_drop';
import { DatasourceDataPanelProps, DataType } from '../types';
import { BucketedAggregation, FieldStatsResponse } from '../../../../../plugins/lens/common';
import { IndexPattern, IndexPatternField } from './types';
import { getColorForDataType, LensFieldIcon } from './lens_field_icon';
import { LensFieldIcon } from './lens_field_icon';
import { trackUiEvent } from '../lens_ui_telemetry';
export interface FieldItemProps {
@ -294,11 +294,6 @@ function FieldItemPopoverContents(props: State & FieldItemProps) {
);
}
const euiButtonColor =
field.type === 'string' ? 'accent' : field.type === 'number' ? 'secondary' : 'primary';
const euiTextColor =
field.type === 'string' ? 'accent' : field.type === 'number' ? 'secondary' : 'default';
const fromDate = DateMath.parse(dateRange.fromDate);
const toDate = DateMath.parse(dateRange.toDate);
@ -391,8 +386,6 @@ function FieldItemPopoverContents(props: State & FieldItemProps) {
const specId = i18n.translate('xpack.lens.indexPattern.fieldStatsCountLabel', {
defaultMessage: 'Count',
});
const expectedColor = getColorForDataType(field.type);
const seriesColors = expectedColor ? [expectedColor] : undefined;
if (field.type === 'date') {
return wrapInPopover(
@ -429,7 +422,6 @@ function FieldItemPopoverContents(props: State & FieldItemProps) {
yAccessors={['count']}
xScaleType={ScaleType.Time}
yScaleType={ScaleType.Linear}
customSeriesColors={seriesColors}
timeZone="local"
/>
</Chart>
@ -453,7 +445,6 @@ function FieldItemPopoverContents(props: State & FieldItemProps) {
yAccessors={['count']}
xScaleType={ScaleType.Linear}
yScaleType={ScaleType.Linear}
customSeriesColors={seriesColors}
/>
</Chart>
);
@ -486,7 +477,7 @@ function FieldItemPopoverContents(props: State & FieldItemProps) {
)}
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiText size="xs" textAlign="left" color={euiTextColor}>
<EuiText size="xs" textAlign="left" color="accent">
{Math.round((topValue.count / props.sampledValues!) * 100)}%
</EuiText>
</EuiFlexItem>
@ -497,7 +488,7 @@ function FieldItemPopoverContents(props: State & FieldItemProps) {
value={topValue.count / props.sampledValues!}
max={1}
size="s"
color={euiButtonColor}
color="accent"
/>
</div>
);

View file

@ -11,19 +11,14 @@
*/
import React from 'react';
import { shallow } from 'enzyme';
import { LensFieldIcon, getColorForDataType } from './lens_field_icon';
import { LensFieldIcon } from './lens_field_icon';
test('LensFieldIcon renders properly', () => {
const component = shallow(<LensFieldIcon type={'date'} />);
expect(component).toMatchSnapshot();
});
test('LensFieldIcon getColorForDataType for a valid type', () => {
const color = getColorForDataType('date');
expect(color).toEqual('#DA8B45');
});
test('LensFieldIcon getColorForDataType for an invalid type', () => {
const color = getColorForDataType('invalid');
expect(color).toEqual('#54B399');
test('LensFieldIcon accepts FieldIcon props', () => {
const component = shallow(<LensFieldIcon type={'date'} fill={'none'} />);
expect(component).toMatchSnapshot();
});

View file

@ -5,26 +5,16 @@
*/
import React from 'react';
import { euiPaletteColorBlind } from '@elastic/eui';
import { FieldIcon, typeToEuiIconMap } from '../../../../../../src/plugins/kibana_react/public';
import { FieldIcon, FieldIconProps } from '../../../../../../src/plugins/kibana_react/public';
import { DataType } from '../types';
import { normalizeOperationDataType } from './utils';
export function getColorForDataType(type: string) {
const iconMap = typeToEuiIconMap[normalizeOperationDataType(type as DataType)];
if (iconMap) {
return iconMap.color;
}
return euiPaletteColorBlind()[0];
}
export function LensFieldIcon({ type }: { type: DataType }) {
export function LensFieldIcon({ type, ...rest }: FieldIconProps & { type: DataType }) {
return (
<FieldIcon
type={normalizeOperationDataType(type)}
className="lnsFieldListPanel__fieldIcon"
size="m"
useColor
type={normalizeOperationDataType(type)}
{...rest}
/>
);
}

View file

@ -23,7 +23,7 @@ exports[`Should remove selected fields from selectable 1`] = `
id="addTooltipFieldPopover"
isOpen={false}
ownFocus={true}
panelPaddingSize="m"
panelPaddingSize="none"
>
<EuiSelectable
onChange={[Function]}
@ -32,14 +32,19 @@ exports[`Should remove selected fields from selectable 1`] = `
Object {
"label": "@timestamp",
"prepend": <FieldIcon
size="m"
className="eui-alignMiddle"
fill="none"
type="date"
useColor={true}
/>,
"value": "@timestamp",
},
]
}
searchProps={
Object {
"compressed": true,
}
}
searchable={true}
singleSelection={false}
>
@ -88,7 +93,7 @@ exports[`Should render 1`] = `
id="addTooltipFieldPopover"
isOpen={false}
ownFocus={true}
panelPaddingSize="m"
panelPaddingSize="none"
>
<EuiSelectable
onChange={[Function]}
@ -97,32 +102,37 @@ exports[`Should render 1`] = `
Object {
"label": "@timestamp",
"prepend": <FieldIcon
size="m"
className="eui-alignMiddle"
fill="none"
type="date"
useColor={true}
/>,
"value": "@timestamp",
},
Object {
"label": "custom label for prop1",
"prepend": <FieldIcon
size="m"
className="eui-alignMiddle"
fill="none"
type="string"
useColor={true}
/>,
"value": "prop1",
},
Object {
"label": "prop2",
"prepend": <FieldIcon
size="m"
className="eui-alignMiddle"
fill="none"
type="string"
useColor={true}
/>,
"value": "prop2",
},
]
}
searchProps={
Object {
"compressed": true,
}
}
searchable={true}
singleSelection={false}
>

View file

@ -39,7 +39,10 @@ function getOptions(fields, selectedFields) {
.map(field => {
return {
value: field.name,
prepend: 'type' in field ? <FieldIcon type={field.type} size="m" useColor /> : null,
prepend:
'type' in field ? (
<FieldIcon className="eui-alignMiddle" type={field.type} fill="none" />
) : null,
label: 'label' in field ? field.label : field.name,
};
})
@ -127,7 +130,12 @@ export class AddTooltipFieldPopover extends Component {
return (
<Fragment>
<EuiSelectable searchable options={this.state.options} onChange={this._onSelect}>
<EuiSelectable
searchable
searchProps={{ compressed: true }}
options={this.state.options}
onChange={this._onSelect}
>
{(list, search) => (
<div style={{ width: '300px' }}>
<EuiPopoverTitle>{search}</EuiPopoverTitle>
@ -161,6 +169,7 @@ export class AddTooltipFieldPopover extends Component {
button={this._renderAddButton()}
isOpen={this.state.isPopoverOpen}
closePopover={this._closePopover}
panelPaddingSize="none"
ownFocus
>
{this._renderContent()}

View file

@ -8,7 +8,7 @@ import _ from 'lodash';
import PropTypes from 'prop-types';
import React from 'react';
import { EuiComboBox, EuiHighlight } from '@elastic/eui';
import { EuiComboBox, EuiHighlight, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { FieldIcon } from '../../../../../../src/plugins/kibana_react/public';
function fieldsToOptions(fields) {
@ -30,11 +30,14 @@ function fieldsToOptions(fields) {
function renderOption(option, searchValue, contentClassName) {
return (
<span className={contentClassName}>
<FieldIcon type={option.value.type} size="m" useColor />
&nbsp;
<EuiHighlight search={searchValue}>{option.label}</EuiHighlight>
</span>
<EuiFlexGroup className={contentClassName} gutterSize="s" alignItems="center">
<EuiFlexItem grow={null}>
<FieldIcon type={option.value.type} fill="none" />
</EuiFlexItem>
<EuiFlexItem>
<EuiHighlight search={searchValue}>{option.label}</EuiHighlight>
</EuiFlexItem>
</EuiFlexGroup>
);
}

View file

@ -7,18 +7,21 @@
import PropTypes from 'prop-types';
import React from 'react';
import { EuiComboBox, EuiHighlight } from '@elastic/eui';
import { EuiComboBox, EuiHighlight, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { FIELD_ORIGIN } from '../../../../../common/constants';
import { i18n } from '@kbn/i18n';
import { FieldIcon } from '../../../../../../../../../src/plugins/kibana_react/public';
function renderOption(option, searchValue, contentClassName) {
return (
<span className={contentClassName}>
<FieldIcon type={option.value.type} size="m" useColor />
&nbsp;
<EuiHighlight search={searchValue}>{option.label}</EuiHighlight>
</span>
<EuiFlexGroup className={contentClassName} gutterSize="s" alignItems="center">
<EuiFlexItem grow={null}>
<FieldIcon type={option.value.type} fill="none" />
</EuiFlexItem>
<EuiFlexItem>
<EuiHighlight search={searchValue}>{option.label}</EuiHighlight>
</EuiFlexItem>
</EuiFlexGroup>
);
}