diff --git a/src/plugins/data/common/es_query/filters/get_display_value.ts b/src/plugins/data/common/es_query/filters/get_display_value.ts index 4bf7e1c9c6ba7..03167f3080419 100644 --- a/src/plugins/data/common/es_query/filters/get_display_value.ts +++ b/src/plugins/data/common/es_query/filters/get_display_value.ts @@ -18,6 +18,7 @@ */ import { get } from 'lodash'; +import { i18n } from '@kbn/i18n'; import { IIndexPattern, IFieldType } from '../..'; import { getIndexPatternFromFilter } from './get_index_pattern_from_filter'; import { Filter } from '../filters'; @@ -27,7 +28,16 @@ function getValueFormatter(indexPattern?: IIndexPattern, key?: string) { let format = get(indexPattern, ['fields', 'byName', key, 'format']); if (!format && (indexPattern.fields as any).getByName) { // TODO: Why is indexPatterns sometimes a map and sometimes an array? - format = ((indexPattern.fields as any).getByName(key) as IFieldType).format; + const field: IFieldType = (indexPattern.fields as any).getByName(key); + if (!field) { + throw new Error( + i18n.translate('data.filter.filterBar.fieldNotFound', { + defaultMessage: 'Field {key} not found in index pattern {indexPattern}', + values: { key, indexPattern: indexPattern.title }, + }) + ); + } + format = field.format; } return format; } diff --git a/src/plugins/data/public/ui/filter_bar/_global_filter_item.scss b/src/plugins/data/public/ui/filter_bar/_global_filter_item.scss index 51204e2a61168..24adf0093af95 100644 --- a/src/plugins/data/public/ui/filter_bar/_global_filter_item.scss +++ b/src/plugins/data/public/ui/filter_bar/_global_filter_item.scss @@ -32,6 +32,15 @@ font-style: italic; } +.globalFilterItem-isInvalid { + text-decoration: none; + + .globalFilterLabel__value { + color: $euiColorDanger; + font-weight: $euiFontWeightBold; + } +} + .globalFilterItem-isPinned { position: relative; diff --git a/src/plugins/data/public/ui/filter_bar/filter_editor/lib/filter_label.tsx b/src/plugins/data/public/ui/filter_bar/filter_editor/lib/filter_label.tsx index 49a0d6f2ab3bd..503650d13c5f8 100644 --- a/src/plugins/data/public/ui/filter_bar/filter_editor/lib/filter_label.tsx +++ b/src/plugins/data/public/ui/filter_bar/filter_editor/lib/filter_label.tsx @@ -41,6 +41,10 @@ export function FilterLabel({ filter, valueLabel }: Props) { prefixText ); + const getValue = (text?: string) => { + return {text}; + }; + if (filter.meta.alias !== null) { return ( @@ -55,35 +59,35 @@ export function FilterLabel({ filter, valueLabel }: Props) { return ( {prefix} - {filter.meta.key} {existsOperator.message} + {filter.meta.key}: {getValue(`${existsOperator.message}`)} ); case esFilters.FILTERS.GEO_BOUNDING_BOX: return ( {prefix} - {filter.meta.key}: {valueLabel} + {filter.meta.key}: {getValue(valueLabel)} ); case esFilters.FILTERS.GEO_POLYGON: return ( {prefix} - {filter.meta.key}: {valueLabel} + {filter.meta.key}: {getValue(valueLabel)} ); case esFilters.FILTERS.PHRASES: return ( {prefix} - {filter.meta.key} {isOneOfOperator.message} {valueLabel} + {filter.meta.key}: {getValue(`${isOneOfOperator.message} ${valueLabel}`)} ); case esFilters.FILTERS.QUERY_STRING: return ( {prefix} - {valueLabel} + {getValue(`${valueLabel}`)} ); case esFilters.FILTERS.PHRASE: @@ -91,14 +95,14 @@ export function FilterLabel({ filter, valueLabel }: Props) { return ( {prefix} - {filter.meta.key}: {valueLabel} + {filter.meta.key}: {getValue(valueLabel)} ); default: return ( {prefix} - {JSON.stringify(filter.query)} + {getValue(`${JSON.stringify(filter.query) || filter.meta.value}`)} ); } diff --git a/src/plugins/data/public/ui/filter_bar/filter_item.tsx b/src/plugins/data/public/ui/filter_bar/filter_item.tsx index 788663041fd03..f24befff9a9a8 100644 --- a/src/plugins/data/public/ui/filter_bar/filter_item.tsx +++ b/src/plugins/data/public/ui/filter_bar/filter_item.tsx @@ -25,6 +25,7 @@ import { IUiSettingsClient } from 'src/core/public'; import { FilterEditor } from './filter_editor'; import { FilterView } from './filter_view'; import { esFilters, IIndexPattern } from '../..'; +import { getNotifications } from '../../services'; interface Props { id: string; @@ -56,24 +57,41 @@ class FilterItemUI extends Component { public render() { const { filter, id } = this.props; const { negate, disabled } = filter.meta; + let hasError: boolean = false; + + let valueLabel; + try { + valueLabel = esFilters.getDisplayValueFromFilter(filter, this.props.indexPatterns); + } catch (e) { + getNotifications().toasts.addError(e, { + title: this.props.intl.formatMessage({ + id: 'data.filter.filterBar.labelErrorMessage', + defaultMessage: 'Failed to display filter', + }), + }); + valueLabel = this.props.intl.formatMessage({ + id: 'data.filter.filterBar.labelErrorText', + defaultMessage: 'Error', + }); + hasError = true; + } + const dataTestSubjKey = filter.meta.key ? `filter-key-${filter.meta.key}` : ''; + const dataTestSubjValue = filter.meta.value ? `filter-value-${valueLabel}` : ''; + const dataTestSubjDisabled = `filter-${ + this.props.filter.meta.disabled ? 'disabled' : 'enabled' + }`; const classes = classNames( 'globalFilterItem', { - 'globalFilterItem-isDisabled': disabled, + 'globalFilterItem-isDisabled': disabled || hasError, + 'globalFilterItem-isInvalid': hasError, 'globalFilterItem-isPinned': esFilters.isFilterPinned(filter), 'globalFilterItem-isExcluded': negate, }, this.props.className ); - const valueLabel = esFilters.getDisplayValueFromFilter(filter, this.props.indexPatterns); - const dataTestSubjKey = filter.meta.key ? `filter-key-${filter.meta.key}` : ''; - const dataTestSubjValue = filter.meta.value ? `filter-value-${valueLabel}` : ''; - const dataTestSubjDisabled = `filter-${ - this.props.filter.meta.disabled ? 'disabled' : 'enabled' - }`; - const badge = (