[Discover] Show field tokens in the grid header (#167179)

- Resolves https://github.com/elastic/kibana/issues/166906

## Summary

This PR adds field tokens to column header for the grid.

There are also additional necessary changes:
- field utils (name, description, icon) were moved from
`@kbn/unified-field-list` and `@kbn/discover-utils` to its own new
package `@kbn/field-utils`
- Unified Data Table and Unified Doc Viewer got a new prop `columnTypes`
which allows to render correct field icons for ES|QL searches (before
types were derived from the data view fields which could be misleading
in text-based searches as users can customize field names via query)

<img width="600" alt="Screenshot 2023-09-25 at 19 30 21"
src="388de9bb-94f7-4d3e-878a-ca0da99fcec2">


### Checklist

- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [x] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Davis McPhee <davis.mcphee@elastic.co>
This commit is contained in:
Julia Rechkunova 2023-10-05 14:02:13 +02:00 committed by GitHub
parent 56bd62fa2f
commit 4f9e2bdf19
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
87 changed files with 3018 additions and 1037 deletions

1
.github/CODEOWNERS vendored
View file

@ -396,6 +396,7 @@ x-pack/test/functional_execution_context/plugins/alerts @elastic/kibana-core
examples/field_formats_example @elastic/kibana-data-discovery
src/plugins/field_formats @elastic/kibana-data-discovery
packages/kbn-field-types @elastic/kibana-data-discovery
packages/kbn-field-utils @elastic/kibana-data-discovery
x-pack/plugins/file_upload @elastic/kibana-gis
examples/files_example @elastic/appex-sharedux
src/plugins/files_management @elastic/appex-sharedux

View file

@ -48,6 +48,7 @@
"eventAnnotationListing": "src/plugins/event_annotation_listing",
"eventAnnotationCommon": "packages/kbn-event-annotation-common",
"eventAnnotationComponents": "packages/kbn-event-annotation-components",
"fieldUtils": "packages/kbn-field-utils",
"fieldFormats": "src/plugins/field_formats",
"files": "src/plugins/files",
"filesManagement": "src/plugins/files_management",

View file

@ -432,6 +432,7 @@
"@kbn/field-formats-example-plugin": "link:examples/field_formats_example",
"@kbn/field-formats-plugin": "link:src/plugins/field_formats",
"@kbn/field-types": "link:packages/kbn-field-types",
"@kbn/field-utils": "link:packages/kbn-field-utils",
"@kbn/file-upload-plugin": "link:x-pack/plugins/file_upload",
"@kbn/files-example-plugin": "link:examples/files_example",
"@kbn/files-management-plugin": "link:src/plugins/files_management",

View file

@ -16,8 +16,6 @@ export {
ENABLE_ESQL,
FIELDS_LIMIT_SETTING,
HIDE_ANNOUNCEMENTS,
KNOWN_FIELD_TYPE_LIST,
KNOWN_FIELD_TYPES,
MAX_DOC_FIELDS_DISPLAYED,
MODIFY_COLUMNS_ON_SWITCH,
ROW_HEIGHT_OPTION,
@ -36,10 +34,8 @@ export {
formatFieldValue,
formatHit,
getDocId,
getFieldTypeName,
getIgnoredReason,
getShouldShowFieldHandler,
isKnownFieldType,
isNestedFieldParent,
usePager,
} from './src';

View file

@ -8,7 +8,7 @@
import type { SearchHit } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
export type { FieldTypeKnown, IgnoredReason, ShouldShowFieldInTableHandler } from './utils';
export type { IgnoredReason, ShouldShowFieldInTableHandler } from './utils';
export interface EsHitRecord extends Omit<SearchHit, '_source'> {
_source?: Record<string, unknown>;
@ -35,3 +35,14 @@ export interface DataTableRecord {
*/
isAnchor?: boolean;
}
type FormattedHitPair = readonly [
fieldDisplayName: string,
formattedValue: string,
fieldName: string | null // `null` is when number of fields is limited and there is an extra pair about it
];
/**
* Pairs array for each field in the hit
*/
export type FormattedHit = FormattedHitPair[];

View file

@ -41,11 +41,11 @@ describe('formatHit', () => {
fieldFormatsMock
);
expect(formatted).toEqual([
['extension', 'formatted:png'],
['message', 'formatted:foobar'],
['object.value', 'formatted:42,13'],
['_index', 'formatted:logs'],
['_score', undefined],
['extension', 'formatted:png', 'extension'],
['message', 'formatted:foobar', 'message'],
['object.value', 'formatted:42,13', 'object.value'],
['_index', 'formatted:logs', '_index'],
['_score', undefined, '_score'],
]);
});
@ -83,9 +83,9 @@ describe('formatHit', () => {
fieldFormatsMock
);
expect(formatted).toEqual([
['extension', 'formatted:png'],
['message', 'formatted:foobar'],
['and 3 more fields', ''],
['extension', 'formatted:png', 'extension'],
['message', 'formatted:foobar', 'message'],
['and 3 more fields', '', null],
]);
});
@ -98,10 +98,10 @@ describe('formatHit', () => {
fieldFormatsMock
);
expect(formatted).toEqual([
['message', 'formatted:foobar'],
['object.value', 'formatted:42,13'],
['_index', 'formatted:logs'],
['_score', undefined],
['message', 'formatted:foobar', 'message'],
['object.value', 'formatted:42,13', 'object.value'],
['_index', 'formatted:logs', '_index'],
['_score', undefined, '_score'],
]);
});
@ -114,9 +114,9 @@ describe('formatHit', () => {
fieldFormatsMock
);
expect(formatted).toEqual([
['bytesDisplayName', 'formatted:123'],
['_index', 'formatted:logs'],
['_score', undefined],
['bytesDisplayName', 'formatted:123', 'bytes'],
['_index', 'formatted:logs', '_index'],
['_score', undefined, '_score'],
]);
});
});

View file

@ -10,20 +10,23 @@ import type { SearchHit } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'
import { i18n } from '@kbn/i18n';
import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
import type { DataView } from '@kbn/data-views-plugin/public';
import type { DataTableRecord, ShouldShowFieldInTableHandler } from '../types';
import type { DataTableRecord, ShouldShowFieldInTableHandler, FormattedHit } from '../types';
import { formatFieldValue } from './format_value';
const formattedHitCache = new WeakMap<SearchHit, FormattedHit>();
type FormattedHit = Array<readonly [fieldName: string, formattedValue: string]>;
const formattedHitCache = new WeakMap<
SearchHit,
{ formattedHit: FormattedHit; maxEntries: number }
>();
/**
* Returns a formatted document in form of key/value pairs of the fields name and a formatted value.
* The value returned in each pair is an HTML string which is safe to be applied to the DOM, since
* it's formatted using field formatters.
* @param hit The hit to format
* @param dataView The corresponding data view
* @param shouldShowFieldHandler A function to check a field.
* @param hit
* @param dataView
* @param shouldShowFieldHandler
* @param maxEntries
* @param fieldFormats
*/
export function formatHit(
hit: DataTableRecord,
@ -33,40 +36,35 @@ export function formatHit(
fieldFormats: FieldFormatsStart
): FormattedHit {
const cached = formattedHitCache.get(hit.raw);
if (cached) {
return cached;
if (cached && cached.maxEntries === maxEntries) {
return cached.formattedHit;
}
const highlights = hit.raw.highlight ?? {};
// Flatten the object using the flattenHit implementation we use across Discover for flattening documents.
const flattened = hit.flattened;
const highlightPairs: Array<[fieldName: string, formattedValue: string]> = [];
const sourcePairs: Array<[fieldName: string, formattedValue: string]> = [];
const highlightPairs: FormattedHit = [];
const sourcePairs: FormattedHit = [];
// Add each flattened field into the corresponding array for highlighted or other fields,
// depending on whether the original hit had a highlight for it. That way we can later
// put highlighted fields first in the document summary.
Object.entries(flattened).forEach(([key, val]) => {
// Retrieve the (display) name of the fields, if it's a mapped field on the data view
const displayKey = dataView.fields.getByName(key)?.displayName;
const field = dataView.fields.getByName(key);
const displayKey = field?.displayName;
const pairs = highlights[key] ? highlightPairs : sourcePairs;
// Format the raw value using the regular field formatters for that field
const formattedValue = formatFieldValue(
val,
hit.raw,
fieldFormats,
dataView,
dataView.fields.getByName(key)
);
const formattedValue = formatFieldValue(val, hit.raw, fieldFormats, dataView, field);
// If the field was a mapped field, we validate it against the fieldsToShow list, if not
// we always include it into the result.
if (displayKey) {
if (shouldShowFieldHandler(key)) {
pairs.push([displayKey, formattedValue]);
pairs.push([displayKey, formattedValue, key]);
}
} else {
pairs.push([key, formattedValue]);
pairs.push([key, formattedValue, key]);
}
});
const pairs = [...highlightPairs, ...sourcePairs];
@ -83,8 +81,9 @@ export function formatHit(
values: { count: pairs.length - maxEntries },
}),
'',
null,
] as const,
];
formattedHitCache.set(hit.raw, formatted);
formattedHitCache.set(hit.raw, { formattedHit: formatted, maxEntries });
return formatted;
}

View file

@ -7,11 +7,9 @@
*/
export * from './build_data_record';
export * from './field_types';
export * from './format_hit';
export * from './format_value';
export * from './get_doc_id';
export * from './get_field_type_name';
export * from './get_ignored_reason';
export * from './get_should_show_field_handler';
export * from './nested_fields';

View file

@ -9,7 +9,7 @@
export type {
DataTableRecord,
EsHitRecord,
FieldTypeKnown,
IgnoredReason,
ShouldShowFieldInTableHandler,
FormattedHit,
} from './src/types';

View file

@ -9,7 +9,8 @@
import { htmlIdGenerator, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React, { useCallback, useMemo } from 'react';
import { useExistingFieldsReader, getFieldIconType } from '@kbn/unified-field-list';
import { getFieldIconType } from '@kbn/field-utils';
import { useExistingFieldsReader } from '@kbn/unified-field-list';
import {
FieldOption,
FieldOptionValue,

View file

@ -15,10 +15,10 @@
"target/**/*"
],
"kbn_references": [
"@kbn/i18n",
"@kbn/visualization-ui-components",
"@kbn/ui-theme",
"@kbn/chart-icons",
"@kbn/i18n",
"@kbn/visualization-ui-components",
"@kbn/ui-theme",
"@kbn/chart-icons",
"@kbn/unified-field-list",
"@kbn/data-views-plugin",
"@kbn/data-plugin",
@ -28,5 +28,6 @@
"@kbn/i18n-react",
"@kbn/saved-objects-finder-plugin",
"@kbn/expressions-plugin",
"@kbn/field-utils"
]
}

View file

@ -0,0 +1,3 @@
# @kbn/field-utils
Utils for rendering fields

View file

@ -0,0 +1,22 @@
/*
* 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 type { FieldTypeKnown, FieldBase } from './types';
export {
isKnownFieldType,
KNOWN_FIELD_TYPES,
KNOWN_FIELD_TYPE_LIST,
} from './src/utils/field_types';
export { getFieldIconType } from './src/utils/get_field_icon_type';
export { getFieldType } from './src/utils/get_field_type';
export { getFieldTypeDescription } from './src/utils/get_field_type_description';
export { getFieldTypeName, UNKNOWN_FIELD_TYPE_MESSAGE } from './src/utils/get_field_type_name';
export { FieldIcon, type FieldIconProps, getFieldIconProps } from './src/components/field_icon';

View file

@ -6,6 +6,8 @@
* Side Public License, v 1.
*/
export { getFieldTypeDescription } from './get_field_type_description';
export { getFieldType } from './get_field_type';
export { getFieldIconType } from './get_field_icon_type';
module.exports = {
preset: '@kbn/test',
rootDir: '../..',
roots: ['<rootDir>/packages/kbn-field-utils'],
};

View file

@ -0,0 +1,5 @@
{
"type": "shared-common",
"id": "@kbn/field-utils",
"owner": "@elastic/kibana-data-discovery"
}

View file

@ -0,0 +1,7 @@
{
"name": "@kbn/field-utils",
"private": true,
"version": "1.0.0",
"license": "SSPL-1.0 OR Elastic License 2.0",
"sideEffects": false
}

View file

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`UnifiedFieldList <FieldIcon /> accepts additional props 1`] = `
exports[`FieldUtils <FieldIcon /> accepts additional props 1`] = `
<FieldIcon
fill="none"
label="Date"
@ -9,14 +9,14 @@ exports[`UnifiedFieldList <FieldIcon /> accepts additional props 1`] = `
/>
`;
exports[`UnifiedFieldList <FieldIcon /> renders Document type properly 1`] = `
exports[`FieldUtils <FieldIcon /> renders Document type properly 1`] = `
<FieldIcon
label="Records"
type="number"
/>
`;
exports[`UnifiedFieldList <FieldIcon /> renders properly 1`] = `
exports[`FieldUtils <FieldIcon /> renders properly 1`] = `
<FieldIcon
label="Date"
scripted={false}
@ -24,7 +24,7 @@ exports[`UnifiedFieldList <FieldIcon /> renders properly 1`] = `
/>
`;
exports[`UnifiedFieldList <FieldIcon /> renders properly scripted fields 1`] = `
exports[`FieldUtils <FieldIcon /> renders properly scripted fields 1`] = `
<FieldIcon
label="Date"
scripted={true}

View file

@ -15,7 +15,7 @@ import { getFieldIconProps } from './get_field_icon_props';
const dateField = dataView.getFieldByName('@timestamp')!;
const scriptedField = dataView.getFieldByName('script date')!;
describe('UnifiedFieldList <FieldIcon />', () => {
describe('FieldUtils <FieldIcon />', () => {
test('renders properly', () => {
const component = shallow(<FieldIcon {...getFieldIconProps(dateField)} />);
expect(component).toMatchSnapshot();

View file

@ -8,7 +8,7 @@
import React from 'react';
import { FieldIcon as KbnFieldIcon, FieldIconProps as KbnFieldIconProps } from '@kbn/react-field';
import { getFieldTypeName } from '@kbn/discover-utils';
import { getFieldTypeName } from '../../utils/get_field_type_name';
export type FieldIconProps = KbnFieldIconProps;

View file

@ -6,14 +6,12 @@
* Side Public License, v 1.
*/
import { type DataViewField } from '@kbn/data-views-plugin/common';
import { FieldListItem } from '../../types';
import { getFieldIconType } from '../../utils/field_types';
import { type FieldIconProps } from './field_icon';
import type { DataViewField } from '@kbn/data-views-plugin/common';
import type { FieldIconProps } from './field_icon';
import { getFieldIconType } from '../../utils/get_field_icon_type';
import type { FieldBase } from '../../types';
export function getFieldIconProps<T extends FieldListItem = DataViewField>(
field: T
): FieldIconProps {
export function getFieldIconProps<T extends FieldBase = DataViewField>(field: T): FieldIconProps {
return {
type: getFieldIconType(field),
scripted: field.scripted,

View file

@ -9,13 +9,13 @@
import React, { Fragment } from 'react';
import { type DataViewField } from '@kbn/data-views-plugin/common';
import type { FieldIconProps, GenericFieldIcon } from './field_icon';
import { type FieldListItem } from '../../types';
import { type FieldBase } from '../../types';
const Fallback = () => <Fragment />;
const LazyFieldIcon = React.lazy(() => import('./field_icon')) as GenericFieldIcon;
function WrappedFieldIcon<T extends FieldListItem = DataViewField>(props: FieldIconProps) {
function WrappedFieldIcon<T extends FieldBase = DataViewField>(props: FieldIconProps) {
return (
<React.Suspense fallback={<Fallback />}>
<LazyFieldIcon {...props} />

View file

@ -0,0 +1,26 @@
/*
* 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 { DataViewField } from '@kbn/data-views-plugin/common';
export type FieldTypeKnown = Exclude<
DataViewField['timeSeriesMetric'] | DataViewField['type'],
undefined
>;
export interface FieldBase {
name: DataViewField['name'];
type?: DataViewField['type'];
displayName?: DataViewField['displayName'];
count?: DataViewField['count'];
timeSeriesMetric?: DataViewField['timeSeriesMetric'];
esTypes?: DataViewField['esTypes'];
scripted?: DataViewField['scripted'];
}
export type GetCustomFieldType<T extends FieldBase> = (field: T) => FieldTypeKnown;

View file

@ -6,12 +6,7 @@
* Side Public License, v 1.
*/
import type { DataViewField } from '@kbn/data-views-plugin/common';
export type FieldTypeKnown = Exclude<
DataViewField['timeSeriesMetric'] | DataViewField['type'],
undefined
>;
import { type FieldTypeKnown } from '../types';
/**
* Field types for which name and description are defined

View file

@ -6,10 +6,10 @@
* Side Public License, v 1.
*/
import { DataViewField } from '@kbn/data-views-plugin/common';
import type { DataViewField } from '@kbn/data-views-plugin/common';
import { getFieldIconType } from './get_field_icon_type';
describe('UnifiedFieldList getFieldIconType()', () => {
describe('FieldUtils getFieldIconType()', () => {
it('extracts type for non-string types', () => {
expect(
getFieldIconType({

View file

@ -7,9 +7,9 @@
*/
import { type DataViewField } from '@kbn/data-views-plugin/common';
import { isKnownFieldType } from '@kbn/discover-utils';
import type { FieldListItem, GetCustomFieldType } from '../../types';
import { isKnownFieldType } from './field_types';
import { getFieldType } from './get_field_type';
import type { FieldBase, GetCustomFieldType } from '../types';
/**
* Returns an icon type for a field
@ -17,7 +17,7 @@ import { getFieldType } from './get_field_type';
* @param getCustomFieldType
* @public
*/
export function getFieldIconType<T extends FieldListItem = DataViewField>(
export function getFieldIconType<T extends FieldBase = DataViewField>(
field: T,
getCustomFieldType?: GetCustomFieldType<T>
): string {

View file

@ -9,7 +9,7 @@
import { DataViewField } from '@kbn/data-views-plugin/common';
import { getFieldType } from './get_field_type';
describe('UnifiedFieldList getFieldType()', () => {
describe('FieldUtils getFieldType()', () => {
it('uses time series metric if set', () => {
expect(
getFieldType({

View file

@ -7,14 +7,13 @@
*/
import { type DataViewField } from '@kbn/data-views-plugin/common';
import type { FieldTypeKnown } from '@kbn/discover-utils/types';
import type { FieldListItem } from '../../types';
import type { FieldBase, FieldTypeKnown } from '../types';
/**
* Returns a field type. Time series metric type will override the original field type.
* @param field
*/
export function getFieldType<T extends FieldListItem = DataViewField>(field: T): FieldTypeKnown {
export function getFieldType<T extends FieldBase = DataViewField>(field: T): FieldTypeKnown {
const timeSeriesMetric = field.timeSeriesMetric;
if (timeSeriesMetric) {
return timeSeriesMetric;

View file

@ -7,9 +7,9 @@
*/
import { getFieldTypeDescription, UNKNOWN_FIELD_TYPE_DESC } from './get_field_type_description';
import { KNOWN_FIELD_TYPES } from '@kbn/discover-utils';
import { KNOWN_FIELD_TYPES } from './field_types';
describe('UnifiedFieldList getFieldTypeDescription()', () => {
describe('FieldUtils getFieldTypeDescription()', () => {
describe('known field types should be recognized', () => {
it.each(Object.values(KNOWN_FIELD_TYPES))(
`'%s' should return a string that does not match '${UNKNOWN_FIELD_TYPE_DESC}'`,

View file

@ -8,13 +8,13 @@
import { i18n } from '@kbn/i18n';
import { KBN_FIELD_TYPES } from '@kbn/field-types';
import { KNOWN_FIELD_TYPES } from '@kbn/discover-utils';
import { KNOWN_FIELD_TYPES } from './field_types';
/**
* A user-friendly description of an unknown field type
*/
export const UNKNOWN_FIELD_TYPE_DESC = i18n.translate(
'unifiedFieldList.fieldNameDescription.unknownField',
'fieldUtils.fieldNameDescription.unknownField',
{
defaultMessage: 'Unknown field',
}
@ -33,110 +33,110 @@ export function getFieldTypeDescription(type?: string) {
const knownType: KNOWN_FIELD_TYPES = type as KNOWN_FIELD_TYPES;
switch (knownType) {
case KNOWN_FIELD_TYPES.DOCUMENT:
return i18n.translate('unifiedFieldList.fieldNameDescription.recordField', {
return i18n.translate('fieldUtils.fieldNameDescription.recordField', {
defaultMessage: 'Count of records.',
});
case KNOWN_FIELD_TYPES.BINARY:
return i18n.translate('unifiedFieldList.fieldNameDescription.binaryField', {
return i18n.translate('fieldUtils.fieldNameDescription.binaryField', {
defaultMessage: 'Binary value encoded as a Base64 string.',
});
case KNOWN_FIELD_TYPES.BOOLEAN:
return i18n.translate('unifiedFieldList.fieldNameDescription.booleanField', {
return i18n.translate('fieldUtils.fieldNameDescription.booleanField', {
defaultMessage: 'True and false values.',
});
case KNOWN_FIELD_TYPES.CONFLICT:
return i18n.translate('unifiedFieldList.fieldNameDescription.conflictField', {
return i18n.translate('fieldUtils.fieldNameDescription.conflictField', {
defaultMessage: 'Field has values of different types. Resolve in Management > Data Views.',
});
case KNOWN_FIELD_TYPES.COUNTER:
return i18n.translate('unifiedFieldList.fieldNameDescription.counterField', {
return i18n.translate('fieldUtils.fieldNameDescription.counterField', {
defaultMessage:
'A number that only increases or resets to 0 (zero). Available only for numeric and aggregate_metric_double fields.',
});
case KNOWN_FIELD_TYPES.DATE:
return i18n.translate('unifiedFieldList.fieldNameDescription.dateField', {
return i18n.translate('fieldUtils.fieldNameDescription.dateField', {
defaultMessage: 'A date string or the number of seconds or milliseconds since 1/1/1970.',
});
case KNOWN_FIELD_TYPES.DATE_RANGE:
return i18n.translate('unifiedFieldList.fieldNameDescription.dateRangeField', {
return i18n.translate('fieldUtils.fieldNameDescription.dateRangeField', {
defaultMessage: 'Range of date values.',
});
case KNOWN_FIELD_TYPES.DENSE_VECTOR:
return i18n.translate('unifiedFieldList.fieldNameDescription.denseVectorField', {
return i18n.translate('fieldUtils.fieldNameDescription.denseVectorField', {
defaultMessage: 'Records dense vectors of float values.',
});
case KNOWN_FIELD_TYPES.GAUGE:
return i18n.translate('unifiedFieldList.fieldNameDescription.gaugeField', {
return i18n.translate('fieldUtils.fieldNameDescription.gaugeField', {
defaultMessage:
'A number that can increase or decrease. Available only for numeric and aggregate_metric_double fields.',
});
case KNOWN_FIELD_TYPES.GEO_POINT:
return i18n.translate('unifiedFieldList.fieldNameDescription.geoPointField', {
return i18n.translate('fieldUtils.fieldNameDescription.geoPointField', {
defaultMessage: 'Latitude and longitude points.',
});
case KNOWN_FIELD_TYPES.GEO_SHAPE:
return i18n.translate('unifiedFieldList.fieldNameDescription.geoShapeField', {
return i18n.translate('fieldUtils.fieldNameDescription.geoShapeField', {
defaultMessage: 'Complex shapes, such as polygons.',
});
case KNOWN_FIELD_TYPES.HISTOGRAM:
return i18n.translate('unifiedFieldList.fieldNameDescription.histogramField', {
return i18n.translate('fieldUtils.fieldNameDescription.histogramField', {
defaultMessage: 'Pre-aggregated numerical values in the form of a histogram.',
});
case KNOWN_FIELD_TYPES.IP:
return i18n.translate('unifiedFieldList.fieldNameDescription.ipAddressField', {
return i18n.translate('fieldUtils.fieldNameDescription.ipAddressField', {
defaultMessage: 'IPv4 and IPv6 addresses.',
});
case KNOWN_FIELD_TYPES.IP_RANGE:
return i18n.translate('unifiedFieldList.fieldNameDescription.ipAddressRangeField', {
return i18n.translate('fieldUtils.fieldNameDescription.ipAddressRangeField', {
defaultMessage: 'Range of ip values supporting either IPv4 or IPv6 (or mixed) addresses.',
});
case KNOWN_FIELD_TYPES.FLATTENED:
return i18n.translate('unifiedFieldList.fieldNameDescription.flattenedField', {
return i18n.translate('fieldUtils.fieldNameDescription.flattenedField', {
defaultMessage: 'An entire JSON object as a single field value.',
});
case KNOWN_FIELD_TYPES.MURMUR3:
return i18n.translate('unifiedFieldList.fieldNameDescription.murmur3Field', {
return i18n.translate('fieldUtils.fieldNameDescription.murmur3Field', {
defaultMessage: 'Field that computes and stores hashes of values.',
});
case KNOWN_FIELD_TYPES.NUMBER:
return i18n.translate('unifiedFieldList.fieldNameDescription.numberField', {
return i18n.translate('fieldUtils.fieldNameDescription.numberField', {
defaultMessage: 'Long, integer, short, byte, double, and float values.',
});
case KNOWN_FIELD_TYPES.RANK_FEATURE:
return i18n.translate('unifiedFieldList.fieldNameDescription.rankFeatureField', {
return i18n.translate('fieldUtils.fieldNameDescription.rankFeatureField', {
defaultMessage: 'Records a numeric feature to boost hits at query time.',
});
case KNOWN_FIELD_TYPES.RANK_FEATURES:
return i18n.translate('unifiedFieldList.fieldNameDescription.rankFeaturesField', {
return i18n.translate('fieldUtils.fieldNameDescription.rankFeaturesField', {
defaultMessage: 'Records numeric features to boost hits at query time.',
});
case KNOWN_FIELD_TYPES.POINT:
return i18n.translate('unifiedFieldList.fieldNameDescription.pointField', {
return i18n.translate('fieldUtils.fieldNameDescription.pointField', {
defaultMessage: 'Arbitrary cartesian points.',
});
case KNOWN_FIELD_TYPES.SHAPE:
return i18n.translate('unifiedFieldList.fieldNameDescription.shapeField', {
return i18n.translate('fieldUtils.fieldNameDescription.shapeField', {
defaultMessage: 'Arbitrary cartesian geometries.',
});
case KNOWN_FIELD_TYPES.STRING:
return i18n.translate('unifiedFieldList.fieldNameDescription.stringField', {
return i18n.translate('fieldUtils.fieldNameDescription.stringField', {
defaultMessage: 'Full text such as the body of an email or a product description.',
});
case KNOWN_FIELD_TYPES.TEXT:
return i18n.translate('unifiedFieldList.fieldNameDescription.textField', {
return i18n.translate('fieldUtils.fieldNameDescription.textField', {
defaultMessage: 'Full text such as the body of an email or a product description.',
});
case KNOWN_FIELD_TYPES.KEYWORD:
return i18n.translate('unifiedFieldList.fieldNameDescription.keywordField', {
return i18n.translate('fieldUtils.fieldNameDescription.keywordField', {
defaultMessage:
'Structured content such as an ID, email address, hostname, status code, or tag.',
});
case KNOWN_FIELD_TYPES.NESTED:
return i18n.translate('unifiedFieldList.fieldNameDescription.nestedField', {
return i18n.translate('fieldUtils.fieldNameDescription.nestedField', {
defaultMessage: 'JSON object that preserves the relationship between its subfields.',
});
case KNOWN_FIELD_TYPES.VERSION:
return i18n.translate('unifiedFieldList.fieldNameDescription.versionField', {
return i18n.translate('fieldUtils.fieldNameDescription.versionField', {
defaultMessage: 'Software versions. Supports "Semantic Versioning" precedence rules.',
});
default:

View file

@ -9,7 +9,7 @@
import { getFieldTypeName, UNKNOWN_FIELD_TYPE_MESSAGE } from './get_field_type_name';
import { KNOWN_FIELD_TYPES } from './field_types';
describe('UnifiedFieldList getFieldTypeName()', () => {
describe('FieldUtils getFieldTypeName()', () => {
describe('known field types should be recognized', () => {
it.each(Object.values(KNOWN_FIELD_TYPES))(
`'%s' should return a string that does not match '${UNKNOWN_FIELD_TYPE_MESSAGE}'`,

View file

@ -14,7 +14,7 @@ import { KNOWN_FIELD_TYPES } from './field_types';
* A user-friendly name of an unknown field type
*/
export const UNKNOWN_FIELD_TYPE_MESSAGE = i18n.translate(
'discover.fieldNameIcons.unknownFieldAriaLabel',
'fieldUtils.fieldNameIcons.unknownFieldAriaLabel',
{
defaultMessage: 'Unknown field',
}
@ -32,7 +32,7 @@ export function getFieldTypeName(type?: string) {
if (type === 'source') {
// Note that this type is currently not provided, type for _source is undefined
return i18n.translate('discover.fieldNameIcons.sourceFieldAriaLabel', {
return i18n.translate('fieldUtils.fieldNameIcons.sourceFieldAriaLabel', {
defaultMessage: 'Source field',
});
}
@ -40,107 +40,107 @@ export function getFieldTypeName(type?: string) {
const knownType: KNOWN_FIELD_TYPES = type as KNOWN_FIELD_TYPES;
switch (knownType) {
case KNOWN_FIELD_TYPES.DOCUMENT:
return i18n.translate('discover.fieldNameIcons.recordAriaLabel', {
return i18n.translate('fieldUtils.fieldNameIcons.recordAriaLabel', {
defaultMessage: 'Records',
});
case KNOWN_FIELD_TYPES.BINARY:
return i18n.translate('discover.fieldNameIcons.binaryAriaLabel', {
return i18n.translate('fieldUtils.fieldNameIcons.binaryAriaLabel', {
defaultMessage: 'Binary',
});
case KNOWN_FIELD_TYPES.BOOLEAN:
return i18n.translate('discover.fieldNameIcons.booleanAriaLabel', {
return i18n.translate('fieldUtils.fieldNameIcons.booleanAriaLabel', {
defaultMessage: 'Boolean',
});
case KNOWN_FIELD_TYPES.CONFLICT:
return i18n.translate('discover.fieldNameIcons.conflictFieldAriaLabel', {
return i18n.translate('fieldUtils.fieldNameIcons.conflictFieldAriaLabel', {
defaultMessage: 'Conflict',
});
case KNOWN_FIELD_TYPES.COUNTER:
return i18n.translate('discover.fieldNameIcons.counterFieldAriaLabel', {
return i18n.translate('fieldUtils.fieldNameIcons.counterFieldAriaLabel', {
defaultMessage: 'Counter metric',
});
case KNOWN_FIELD_TYPES.DATE:
return i18n.translate('discover.fieldNameIcons.dateFieldAriaLabel', {
return i18n.translate('fieldUtils.fieldNameIcons.dateFieldAriaLabel', {
defaultMessage: 'Date',
});
case KNOWN_FIELD_TYPES.DATE_RANGE:
return i18n.translate('discover.fieldNameIcons.dateRangeFieldAriaLabel', {
return i18n.translate('fieldUtils.fieldNameIcons.dateRangeFieldAriaLabel', {
defaultMessage: 'Date range',
});
case KNOWN_FIELD_TYPES.DENSE_VECTOR:
return i18n.translate('discover.fieldNameIcons.denseVectorFieldAriaLabel', {
return i18n.translate('fieldUtils.fieldNameIcons.denseVectorFieldAriaLabel', {
defaultMessage: 'Dense vector',
});
case KNOWN_FIELD_TYPES.GAUGE:
return i18n.translate('discover.fieldNameIcons.gaugeFieldAriaLabel', {
return i18n.translate('fieldUtils.fieldNameIcons.gaugeFieldAriaLabel', {
defaultMessage: 'Gauge metric',
});
case KNOWN_FIELD_TYPES.GEO_POINT:
return i18n.translate('discover.fieldNameIcons.geoPointFieldAriaLabel', {
return i18n.translate('fieldUtils.fieldNameIcons.geoPointFieldAriaLabel', {
defaultMessage: 'Geo point',
});
case KNOWN_FIELD_TYPES.GEO_SHAPE:
return i18n.translate('discover.fieldNameIcons.geoShapeFieldAriaLabel', {
return i18n.translate('fieldUtils.fieldNameIcons.geoShapeFieldAriaLabel', {
defaultMessage: 'Geo shape',
});
case KNOWN_FIELD_TYPES.HISTOGRAM:
return i18n.translate('discover.fieldNameIcons.histogramFieldAriaLabel', {
return i18n.translate('fieldUtils.fieldNameIcons.histogramFieldAriaLabel', {
defaultMessage: 'Histogram',
});
case KNOWN_FIELD_TYPES.IP:
return i18n.translate('discover.fieldNameIcons.ipAddressFieldAriaLabel', {
return i18n.translate('fieldUtils.fieldNameIcons.ipAddressFieldAriaLabel', {
defaultMessage: 'IP address',
});
case KNOWN_FIELD_TYPES.IP_RANGE:
return i18n.translate('discover.fieldNameIcons.ipRangeFieldAriaLabel', {
return i18n.translate('fieldUtils.fieldNameIcons.ipRangeFieldAriaLabel', {
defaultMessage: 'IP range',
});
case KNOWN_FIELD_TYPES.FLATTENED:
return i18n.translate('discover.fieldNameIcons.flattenedFieldAriaLabel', {
return i18n.translate('fieldUtils.fieldNameIcons.flattenedFieldAriaLabel', {
defaultMessage: 'Flattened',
});
case KNOWN_FIELD_TYPES.MURMUR3:
return i18n.translate('discover.fieldNameIcons.murmur3FieldAriaLabel', {
return i18n.translate('fieldUtils.fieldNameIcons.murmur3FieldAriaLabel', {
defaultMessage: 'Murmur3',
});
case KNOWN_FIELD_TYPES.NUMBER:
return i18n.translate('discover.fieldNameIcons.numberFieldAriaLabel', {
return i18n.translate('fieldUtils.fieldNameIcons.numberFieldAriaLabel', {
defaultMessage: 'Number',
});
case KNOWN_FIELD_TYPES.RANK_FEATURE:
return i18n.translate('discover.fieldNameIcons.rankFeatureFieldAriaLabel', {
return i18n.translate('fieldUtils.fieldNameIcons.rankFeatureFieldAriaLabel', {
defaultMessage: 'Rank feature',
});
case KNOWN_FIELD_TYPES.RANK_FEATURES:
return i18n.translate('discover.fieldNameIcons.rankFeaturesFieldAriaLabel', {
return i18n.translate('fieldUtils.fieldNameIcons.rankFeaturesFieldAriaLabel', {
defaultMessage: 'Rank features',
});
case KNOWN_FIELD_TYPES.POINT:
return i18n.translate('discover.fieldNameIcons.pointFieldAriaLabel', {
return i18n.translate('fieldUtils.fieldNameIcons.pointFieldAriaLabel', {
defaultMessage: 'Point',
});
case KNOWN_FIELD_TYPES.SHAPE:
return i18n.translate('discover.fieldNameIcons.shapeFieldAriaLabel', {
return i18n.translate('fieldUtils.fieldNameIcons.shapeFieldAriaLabel', {
defaultMessage: 'Shape',
});
case KNOWN_FIELD_TYPES.STRING:
return i18n.translate('discover.fieldNameIcons.stringFieldAriaLabel', {
return i18n.translate('fieldUtils.fieldNameIcons.stringFieldAriaLabel', {
defaultMessage: 'String',
});
case KNOWN_FIELD_TYPES.TEXT:
return i18n.translate('discover.fieldNameIcons.textFieldAriaLabel', {
return i18n.translate('fieldUtils.fieldNameIcons.textFieldAriaLabel', {
defaultMessage: 'Text',
});
case KNOWN_FIELD_TYPES.KEYWORD:
return i18n.translate('discover.fieldNameIcons.keywordFieldAriaLabel', {
return i18n.translate('fieldUtils.fieldNameIcons.keywordFieldAriaLabel', {
defaultMessage: 'Keyword',
});
case KNOWN_FIELD_TYPES.NESTED:
return i18n.translate('discover.fieldNameIcons.nestedFieldAriaLabel', {
return i18n.translate('fieldUtils.fieldNameIcons.nestedFieldAriaLabel', {
defaultMessage: 'Nested',
});
case KNOWN_FIELD_TYPES.VERSION:
return i18n.translate('discover.fieldNameIcons.versionFieldAriaLabel', {
return i18n.translate('fieldUtils.fieldNameIcons.versionFieldAriaLabel', {
defaultMessage: 'Version',
});
default:

View file

@ -0,0 +1,14 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "target/types"
},
"include": ["*.ts", "src/**/*", "__mocks__/**/*.ts"],
"kbn_references": [
"@kbn/i18n",
"@kbn/data-views-plugin",
"@kbn/react-field",
"@kbn/field-types",
],
"exclude": ["target/**/*"]
}

View file

@ -0,0 +1,9 @@
/*
* 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 type { FieldTypeKnown, FieldBase } from './src/types';

View file

@ -9,6 +9,7 @@
export { UnifiedDataTable, DataLoadingState } from './src/components/data_table';
export type { UnifiedDataTableProps } from './src/components/data_table';
export { getDisplayedColumns } from './src/utils/columns';
export { getTextBasedColumnTypes } from './src/utils/get_column_types';
export { ROWS_HEIGHT_OPTIONS } from './src/constants';
export { JSONCodeEditorCommonMemoized } from './src/components/json_code_editor/json_code_editor_common';

File diff suppressed because it is too large Load diff

View file

@ -324,27 +324,36 @@ describe('UnifiedDataTable', () => {
});
it('should render provided in renderDocumentView DocumentView on expand clicked', async () => {
const expandedDoc = {
id: 'test',
raw: {
_index: 'test_i',
_id: 'test',
},
flattened: { test: jest.fn() },
};
const columnTypesOverride = { testField: 'number ' };
const renderDocumentViewMock = jest.fn((hit: DataTableRecord) => (
<div data-test-subj="test-document-view">{hit.id}</div>
));
const component = await getComponent({
...getProps(),
expandedDoc: {
id: 'test',
raw: {
_index: 'test_i',
_id: 'test',
},
flattened: { test: jest.fn() },
},
expandedDoc,
setExpandedDoc: jest.fn(),
renderDocumentView: (
hit: DataTableRecord,
displayedRows: DataTableRecord[],
displayedColumns: string[]
) => <div data-test-subj="test-document-view">{hit.id}</div>,
columnTypes: columnTypesOverride,
renderDocumentView: renderDocumentViewMock,
externalControlColumns: [testLeadingControlColumn],
});
findTestSubject(component, 'docTableExpandToggleColumn').first().simulate('click');
expect(findTestSubject(component, 'test-document-view').exists()).toBeTruthy();
expect(renderDocumentViewMock).toHaveBeenLastCalledWith(
expandedDoc,
getProps().rows,
['_source'],
columnTypesOverride
);
});
describe('externalAdditionalControls', () => {

View file

@ -43,7 +43,11 @@ import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
import type { ThemeServiceStart } from '@kbn/react-kibana-context-common';
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
import type { DocViewFilterFn } from '@kbn/unified-doc-viewer/types';
import { UnifiedDataTableSettings, ValueToStringConverter } from '../types';
import type {
UnifiedDataTableSettings,
ValueToStringConverter,
DataTableColumnTypes,
} from '../types';
import { getDisplayedColumns } from '../utils/columns';
import { convertValueToString } from '../utils/convert_value_to_string';
import { getRowsPerPageOptions } from '../utils/rows_per_page';
@ -75,6 +79,9 @@ interface SortObj {
direction: string;
}
/**
* Unified Data Table props
*/
export interface UnifiedDataTableProps {
/**
* Determines which element labels the grid for ARIA
@ -88,6 +95,16 @@ export interface UnifiedDataTableProps {
* Determines ids of the columns which are displayed
*/
columns: string[];
/**
* If not provided, types will be derived by default from the dataView field types.
* For displaying text-based search results, pass column types (which are available separately in the fetch request) down here.
* Check available utils in `utils/get_column_types.ts`
*/
columnTypes?: DataTableColumnTypes;
/**
* Field tokens could be rendered in column header next to the field name.
*/
showColumnTokens?: boolean;
/**
* If set, the given document is displayed in a flyout
*/
@ -214,7 +231,8 @@ export interface UnifiedDataTableProps {
renderDocumentView?: (
hit: DataTableRecord,
displayedRows: DataTableRecord[],
displayedColumns: string[]
displayedColumns: string[],
columnTypes?: DataTableColumnTypes
) => JSX.Element | undefined;
/**
* Optional value for providing configuration setting for UnifiedDataTable rows height
@ -300,6 +318,8 @@ const CONTROL_COLUMN_IDS_DEFAULT = ['openDetails', 'select'];
export const UnifiedDataTable = ({
ariaLabelledBy,
columns,
columnTypes,
showColumnTokens,
controlColumnIds = CONTROL_COLUMN_IDS_DEFAULT,
dataView,
loadingState,
@ -509,16 +529,16 @@ export const UnifiedDataTable = ({
*/
const renderCellValue = useMemo(
() =>
getRenderCellValueFn(
getRenderCellValueFn({
dataView,
displayedRows,
rows: displayedRows,
useNewFieldsApi,
shouldShowFieldHandler,
() => dataGridRef.current?.closeCellPopover(),
services.fieldFormats,
maxDocFieldsDisplayed,
externalCustomRenderers
),
closePopover: () => dataGridRef.current?.closeCellPopover(),
fieldFormats: services.fieldFormats,
maxEntries: maxDocFieldsDisplayed,
externalCustomRenderers,
}),
[
dataView,
displayedRows,
@ -616,6 +636,8 @@ export const UnifiedDataTable = ({
onFilter,
editField,
visibleCellActions,
columnTypes,
showColumnTokens,
}),
[
onFilter,
@ -633,6 +655,8 @@ export const UnifiedDataTable = ({
valueToStringConverter,
editField,
visibleCellActions,
columnTypes,
showColumnTokens,
]
);
@ -841,7 +865,7 @@ export const UnifiedDataTable = ({
)}
{canSetExpandedDoc &&
expandedDoc &&
renderDocumentView!(expandedDoc, displayedRows, displayedColumns)}
renderDocumentView!(expandedDoc, displayedRows, displayedColumns, columnTypes)}
</span>
</UnifiedDataTableContext.Provider>
);

View file

@ -0,0 +1,113 @@
/*
* 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 React, { ReactElement } from 'react';
import { FieldIcon } from '@kbn/field-utils';
import { mountWithIntl } from '@kbn/test-jest-helpers';
import {
createStubDataView,
stubLogstashDataView,
} from '@kbn/data-views-plugin/common/data_view.stub';
import { DataTableColumnHeader } from './data_table_column_header';
const stubDataViewWithNested = createStubDataView({
spec: {
id: 'index_with_nested',
fields: {
'nested_user.lastname': {
name: 'nested_user.lastname',
esTypes: ['text'],
type: 'string',
aggregatable: false,
searchable: true,
subType: {
nested: {
path: 'nested_user',
},
},
},
'nested_user.lastname.keyword': {
name: 'nested_user.lastname.keyword',
esTypes: ['keyword'],
type: 'string',
aggregatable: true,
searchable: true,
subType: {
multi: {
parent: 'nested_user.lastname',
},
nested: {
path: 'nested_user',
},
},
},
},
title: 'index_with_nested',
},
});
describe('DataTableColumnHeader', function () {
async function mountComponent(element: ReactElement) {
const component = mountWithIntl(element);
// wait for lazy modules
await new Promise((resolve) => setTimeout(resolve, 0));
component.update();
return component;
}
it('should render a correct token', async () => {
const component = await mountComponent(
<DataTableColumnHeader
columnName="bytes"
columnDisplayName="bytesDisplayName"
dataView={stubLogstashDataView}
/>
);
expect(component.text()).toBe('NumberbytesDisplayName');
expect(component.find(FieldIcon).first().prop('type')).toBe('number');
});
it('should render a correct token for a custom column type (in case of text-based queries)', async () => {
const component = await mountComponent(
<DataTableColumnHeader
columnName="bytes"
columnDisplayName="bytesDisplayName"
columnTypes={{
bytes: 'keyword',
}}
dataView={stubLogstashDataView}
/>
);
expect(component.text()).toBe('KeywordbytesDisplayName');
expect(component.find(FieldIcon).first().prop('type')).toBe('keyword');
});
it('should not render a token for Document column', async () => {
const component = await mountComponent(
<DataTableColumnHeader
columnName="_source"
columnDisplayName="Document"
dataView={stubLogstashDataView}
/>
);
expect(component.text()).toBe('Document');
expect(component.find(FieldIcon).exists()).toBe(false);
});
it('should render the nested icon', async () => {
const component = await mountComponent(
<DataTableColumnHeader
columnName="nested_user"
columnDisplayName="Nested User"
dataView={stubDataViewWithNested}
/>
);
expect(component.text()).toBe('NestedNested User');
expect(component.find(FieldIcon).first().prop('type')).toBe('nested');
});
});

View file

@ -0,0 +1,83 @@
/*
* 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 React, { useMemo } from 'react';
import { css } from '@emotion/react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import type { DataView } from '@kbn/data-views-plugin/common';
import { FieldIcon, getFieldIconProps } from '@kbn/field-utils';
import { isNestedFieldParent } from '@kbn/discover-utils';
import type { DataTableColumnTypes } from '../types';
interface DataTableColumnHeaderProps {
dataView: DataView;
columnName: string | null;
columnDisplayName: string;
columnTypes?: DataTableColumnTypes;
}
export const DataTableColumnHeader: React.FC<DataTableColumnHeaderProps> = (props) => {
const { columnDisplayName, columnName, columnTypes, dataView } = props;
const columnToken = useMemo(
() => getRenderedToken({ columnName, columnTypes, dataView }),
[columnName, columnTypes, dataView]
);
return (
<EuiFlexGroup
direction="row"
wrap={false}
responsive={false}
alignItems="center"
gutterSize="xs"
css={css`
.euiDataGridHeaderCell--numeric & {
justify-content: flex-end;
}
`}
>
{columnToken && <EuiFlexItem grow={false}>{columnToken}</EuiFlexItem>}
<EuiFlexItem
grow={false}
className="eui-displayInline eui-textTruncate"
data-test-subj="unifiedDataTableColumnTitle"
>
{columnDisplayName}
</EuiFlexItem>
</EuiFlexGroup>
);
};
function getRenderedToken({
dataView,
columnName,
columnTypes,
}: Pick<DataTableColumnHeaderProps, 'dataView' | 'columnName' | 'columnTypes'>) {
if (!columnName || columnName === '_source') {
return null;
}
// for text-based searches
if (columnTypes) {
return columnTypes[columnName] && columnTypes[columnName] !== 'unknown' ? ( // renders an icon or nothing
<FieldIcon type={columnTypes[columnName]} />
) : null;
}
const dataViewField = dataView.getFieldByName(columnName);
if (dataViewField) {
return <FieldIcon {...getFieldIconProps(dataViewField)} />;
}
if (isNestedFieldParent(columnName, dataView)) {
return <FieldIcon type="nested" />;
}
return null;
}

View file

@ -39,102 +39,7 @@ describe('Data table columns', function () {
servicesMock.dataViewFieldEditor.userPermissions.editIndexPattern(),
onFilter: () => {},
});
expect(actual).toMatchInlineSnapshot(`
Array [
Object {
"actions": Object {
"additional": Array [
Object {
"data-test-subj": "gridCopyColumnNameToClipBoardButton",
"iconProps": Object {
"size": "m",
},
"iconType": "copyClipboard",
"label": <FormattedMessage
defaultMessage="Copy name"
id="unifiedDataTable.grid.copyColumnNameToClipBoardButton"
values={Object {}}
/>,
"onClick": [Function],
"size": "xs",
},
Object {
"data-test-subj": "gridCopyColumnValuesToClipBoardButton",
"iconProps": Object {
"size": "m",
},
"iconType": "copyClipboard",
"label": <FormattedMessage
defaultMessage="Copy column"
id="unifiedDataTable.grid.copyColumnValuesToClipBoardButton"
values={Object {}}
/>,
"onClick": [Function],
"size": "xs",
},
],
"showHide": false,
"showMoveLeft": false,
"showMoveRight": false,
},
"cellActions": Array [
[Function],
[Function],
[Function],
],
"displayAsText": "extension",
"id": "extension",
"isSortable": false,
"schema": "string",
"visibleCellActions": undefined,
},
Object {
"actions": Object {
"additional": Array [
Object {
"data-test-subj": "gridCopyColumnNameToClipBoardButton",
"iconProps": Object {
"size": "m",
},
"iconType": "copyClipboard",
"label": <FormattedMessage
defaultMessage="Copy name"
id="unifiedDataTable.grid.copyColumnNameToClipBoardButton"
values={Object {}}
/>,
"onClick": [Function],
"size": "xs",
},
Object {
"data-test-subj": "gridCopyColumnValuesToClipBoardButton",
"iconProps": Object {
"size": "m",
},
"iconType": "copyClipboard",
"label": <FormattedMessage
defaultMessage="Copy column"
id="unifiedDataTable.grid.copyColumnValuesToClipBoardButton"
values={Object {}}
/>,
"onClick": [Function],
"size": "xs",
},
],
"showHide": false,
"showMoveLeft": false,
"showMoveRight": false,
},
"cellActions": Array [
[Function],
],
"displayAsText": "message",
"id": "message",
"isSortable": false,
"schema": "string",
"visibleCellActions": undefined,
},
]
`);
expect(actual).toMatchSnapshot();
});
it('returns eui grid columns with time column', async () => {
@ -155,174 +60,7 @@ describe('Data table columns', function () {
servicesMock.dataViewFieldEditor.userPermissions.editIndexPattern(),
onFilter: () => {},
});
expect(actual).toMatchInlineSnapshot(`
Array [
Object {
"actions": Object {
"additional": Array [
Object {
"data-test-subj": "gridCopyColumnNameToClipBoardButton",
"iconProps": Object {
"size": "m",
},
"iconType": "copyClipboard",
"label": <FormattedMessage
defaultMessage="Copy name"
id="unifiedDataTable.grid.copyColumnNameToClipBoardButton"
values={Object {}}
/>,
"onClick": [Function],
"size": "xs",
},
Object {
"data-test-subj": "gridCopyColumnValuesToClipBoardButton",
"iconProps": Object {
"size": "m",
},
"iconType": "copyClipboard",
"label": <FormattedMessage
defaultMessage="Copy column"
id="unifiedDataTable.grid.copyColumnValuesToClipBoardButton"
values={Object {}}
/>,
"onClick": [Function],
"size": "xs",
},
],
"showHide": false,
"showMoveLeft": true,
"showMoveRight": true,
},
"cellActions": Array [
[Function],
[Function],
[Function],
],
"display": <div
aria-label="timestamp - this field represents the time that events occurred."
>
<EuiToolTip
content="This field represents the time that events occurred."
delay="regular"
display="inlineBlock"
position="top"
>
<React.Fragment>
timestamp
<EuiIcon
type="clock"
/>
</React.Fragment>
</EuiToolTip>
</div>,
"displayAsText": "timestamp",
"id": "timestamp",
"initialWidth": 212,
"isSortable": true,
"schema": "datetime",
"visibleCellActions": undefined,
},
Object {
"actions": Object {
"additional": Array [
Object {
"data-test-subj": "gridCopyColumnNameToClipBoardButton",
"iconProps": Object {
"size": "m",
},
"iconType": "copyClipboard",
"label": <FormattedMessage
defaultMessage="Copy name"
id="unifiedDataTable.grid.copyColumnNameToClipBoardButton"
values={Object {}}
/>,
"onClick": [Function],
"size": "xs",
},
Object {
"data-test-subj": "gridCopyColumnValuesToClipBoardButton",
"iconProps": Object {
"size": "m",
},
"iconType": "copyClipboard",
"label": <FormattedMessage
defaultMessage="Copy column"
id="unifiedDataTable.grid.copyColumnValuesToClipBoardButton"
values={Object {}}
/>,
"onClick": [Function],
"size": "xs",
},
],
"showHide": Object {
"iconType": "cross",
"label": "Remove column",
},
"showMoveLeft": true,
"showMoveRight": true,
},
"cellActions": Array [
[Function],
[Function],
[Function],
],
"displayAsText": "extension",
"id": "extension",
"isSortable": false,
"schema": "string",
"visibleCellActions": undefined,
},
Object {
"actions": Object {
"additional": Array [
Object {
"data-test-subj": "gridCopyColumnNameToClipBoardButton",
"iconProps": Object {
"size": "m",
},
"iconType": "copyClipboard",
"label": <FormattedMessage
defaultMessage="Copy name"
id="unifiedDataTable.grid.copyColumnNameToClipBoardButton"
values={Object {}}
/>,
"onClick": [Function],
"size": "xs",
},
Object {
"data-test-subj": "gridCopyColumnValuesToClipBoardButton",
"iconProps": Object {
"size": "m",
},
"iconType": "copyClipboard",
"label": <FormattedMessage
defaultMessage="Copy column"
id="unifiedDataTable.grid.copyColumnValuesToClipBoardButton"
values={Object {}}
/>,
"onClick": [Function],
"size": "xs",
},
],
"showHide": Object {
"iconType": "cross",
"label": "Remove column",
},
"showMoveLeft": true,
"showMoveRight": true,
},
"cellActions": Array [
[Function],
],
"displayAsText": "message",
"id": "message",
"isSortable": false,
"schema": "string",
"visibleCellActions": undefined,
},
]
`);
expect(actual).toMatchSnapshot();
});
it('returns eui grid with in memory sorting', async () => {
@ -343,174 +81,7 @@ describe('Data table columns', function () {
servicesMock.dataViewFieldEditor.userPermissions.editIndexPattern(),
onFilter: () => {},
});
expect(actual).toMatchInlineSnapshot(`
Array [
Object {
"actions": Object {
"additional": Array [
Object {
"data-test-subj": "gridCopyColumnNameToClipBoardButton",
"iconProps": Object {
"size": "m",
},
"iconType": "copyClipboard",
"label": <FormattedMessage
defaultMessage="Copy name"
id="unifiedDataTable.grid.copyColumnNameToClipBoardButton"
values={Object {}}
/>,
"onClick": [Function],
"size": "xs",
},
Object {
"data-test-subj": "gridCopyColumnValuesToClipBoardButton",
"iconProps": Object {
"size": "m",
},
"iconType": "copyClipboard",
"label": <FormattedMessage
defaultMessage="Copy column"
id="unifiedDataTable.grid.copyColumnValuesToClipBoardButton"
values={Object {}}
/>,
"onClick": [Function],
"size": "xs",
},
],
"showHide": false,
"showMoveLeft": true,
"showMoveRight": true,
},
"cellActions": Array [
[Function],
[Function],
[Function],
],
"display": <div
aria-label="timestamp - this field represents the time that events occurred."
>
<EuiToolTip
content="This field represents the time that events occurred."
delay="regular"
display="inlineBlock"
position="top"
>
<React.Fragment>
timestamp
<EuiIcon
type="clock"
/>
</React.Fragment>
</EuiToolTip>
</div>,
"displayAsText": "timestamp",
"id": "timestamp",
"initialWidth": 212,
"isSortable": true,
"schema": "datetime",
"visibleCellActions": undefined,
},
Object {
"actions": Object {
"additional": Array [
Object {
"data-test-subj": "gridCopyColumnNameToClipBoardButton",
"iconProps": Object {
"size": "m",
},
"iconType": "copyClipboard",
"label": <FormattedMessage
defaultMessage="Copy name"
id="unifiedDataTable.grid.copyColumnNameToClipBoardButton"
values={Object {}}
/>,
"onClick": [Function],
"size": "xs",
},
Object {
"data-test-subj": "gridCopyColumnValuesToClipBoardButton",
"iconProps": Object {
"size": "m",
},
"iconType": "copyClipboard",
"label": <FormattedMessage
defaultMessage="Copy column"
id="unifiedDataTable.grid.copyColumnValuesToClipBoardButton"
values={Object {}}
/>,
"onClick": [Function],
"size": "xs",
},
],
"showHide": Object {
"iconType": "cross",
"label": "Remove column",
},
"showMoveLeft": true,
"showMoveRight": true,
},
"cellActions": Array [
[Function],
[Function],
[Function],
],
"displayAsText": "extension",
"id": "extension",
"isSortable": true,
"schema": "string",
"visibleCellActions": undefined,
},
Object {
"actions": Object {
"additional": Array [
Object {
"data-test-subj": "gridCopyColumnNameToClipBoardButton",
"iconProps": Object {
"size": "m",
},
"iconType": "copyClipboard",
"label": <FormattedMessage
defaultMessage="Copy name"
id="unifiedDataTable.grid.copyColumnNameToClipBoardButton"
values={Object {}}
/>,
"onClick": [Function],
"size": "xs",
},
Object {
"data-test-subj": "gridCopyColumnValuesToClipBoardButton",
"iconProps": Object {
"size": "m",
},
"iconType": "copyClipboard",
"label": <FormattedMessage
defaultMessage="Copy column"
id="unifiedDataTable.grid.copyColumnValuesToClipBoardButton"
values={Object {}}
/>,
"onClick": [Function],
"size": "xs",
},
],
"showHide": Object {
"iconType": "cross",
"label": "Remove column",
},
"showMoveLeft": true,
"showMoveRight": true,
},
"cellActions": Array [
[Function],
],
"displayAsText": "message",
"id": "message",
"isSortable": true,
"schema": "string",
"visibleCellActions": undefined,
},
]
`);
expect(actual).toMatchSnapshot();
});
});
@ -538,4 +109,54 @@ describe('Data table columns', function () {
expect(actual).toEqual(['timestamp', 'extension', 'message']);
});
});
describe('column tokens', () => {
it('returns eui grid columns with tokens', async () => {
const actual = getEuiGridColumns({
showColumnTokens: true,
columns: columnsWithTimeCol,
settings: {},
dataView: dataViewWithTimefieldMock,
defaultColumns: false,
isSortEnabled: true,
isPlainRecord: false,
valueToStringConverter: dataTableContextMock.valueToStringConverter,
rowsCount: 100,
services: {
uiSettings: servicesMock.uiSettings,
toastNotifications: servicesMock.toastNotifications,
},
hasEditDataViewPermission: () =>
servicesMock.dataViewFieldEditor.userPermissions.editIndexPattern(),
onFilter: () => {},
});
expect(actual).toMatchSnapshot();
});
it('returns eui grid columns with tokens for custom column types', async () => {
const actual = getEuiGridColumns({
showColumnTokens: true,
columnTypes: {
extension: 'number',
message: 'keyword',
},
columns,
settings: {},
dataView: dataViewWithTimefieldMock,
defaultColumns: false,
isSortEnabled: true,
isPlainRecord: false,
valueToStringConverter: dataTableContextMock.valueToStringConverter,
rowsCount: 100,
services: {
uiSettings: servicesMock.uiSettings,
toastNotifications: servicesMock.toastNotifications,
},
hasEditDataViewPermission: () =>
servicesMock.dataViewFieldEditor.userPermissions.editIndexPattern(),
onFilter: () => {},
});
expect(actual).toMatchSnapshot();
});
});
});

View file

@ -20,13 +20,16 @@ import { ToastsStart, IUiSettingsClient } from '@kbn/core/public';
import { DocViewFilterFn } from '@kbn/unified-doc-viewer/types';
import { ExpandButton } from './data_table_expand_button';
import { UnifiedDataTableSettings } from '../types';
import type { ValueToStringConverter } from '../types';
import type { ValueToStringConverter, DataTableColumnTypes } from '../types';
import { buildCellActions } from './default_cell_actions';
import { getSchemaByKbnType } from './data_table_schema';
import { SelectButton } from './data_table_document_selection';
import { defaultTimeColumnWidth } from '../constants';
import { buildCopyColumnNameButton, buildCopyColumnValuesButton } from './build_copy_column_button';
import { buildEditFieldButton } from './build_edit_field_button';
import { DataTableColumnHeader } from './data_table_column_header';
const DataTableColumnHeaderMemoized = React.memo(DataTableColumnHeader);
const openDetails = {
id: 'openDetails',
@ -80,6 +83,8 @@ function buildEuiGridColumn({
editField,
columnCellActions,
visibleCellActions,
columnTypes,
showColumnTokens,
}: {
columnName: string;
columnWidth: number | undefined;
@ -95,6 +100,8 @@ function buildEuiGridColumn({
editField?: (fieldName: string) => void;
columnCellActions?: EuiDataGridColumnCellAction[];
visibleCellActions?: number;
columnTypes?: DataTableColumnTypes;
showColumnTokens?: boolean;
}) {
const dataViewField = dataView.getFieldByName(columnName);
const editFieldButton =
@ -122,6 +129,14 @@ function buildEuiGridColumn({
id: columnName,
schema: getSchemaByKbnType(dataViewField?.type),
isSortable: isSortEnabled && (isPlainRecord || dataViewField?.sortable === true),
display: showColumnTokens ? (
<DataTableColumnHeaderMemoized
dataView={dataView}
columnName={columnName}
columnDisplayName={columnDisplayName}
columnTypes={columnTypes}
/>
) : undefined,
displayAsText: columnDisplayName,
actions: {
showHide:
@ -203,6 +218,8 @@ export function getEuiGridColumns({
onFilter,
editField,
visibleCellActions,
columnTypes,
showColumnTokens,
}: {
columns: string[];
columnsCellActions?: EuiDataGridColumnCellAction[][];
@ -221,6 +238,8 @@ export function getEuiGridColumns({
onFilter: DocViewFilterFn;
editField?: (fieldName: string) => void;
visibleCellActions?: number;
columnTypes?: DataTableColumnTypes;
showColumnTokens?: boolean;
}) {
const getColWidth = (column: string) => settings?.columns?.[column]?.width ?? 0;
@ -240,6 +259,8 @@ export function getEuiGridColumns({
onFilter,
editField,
visibleCellActions,
columnTypes,
showColumnTokens,
})
);
}

View file

@ -22,3 +22,8 @@ export type ValueToStringConverter = (
columnId: string,
options?: { compatibleWithCSV?: boolean }
) => { formattedString: string; withFormula: boolean };
/**
* Custom column types per column name
*/
export type DataTableColumnTypes = Record<string, string>;

View file

@ -0,0 +1,46 @@
/*
* 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 { getTextBasedColumnTypes } from './get_column_types';
describe('getColumnTypes', () => {
describe('getTextBasedColumnTypes', () => {
test('returns a correct column types map', async () => {
const result = getTextBasedColumnTypes([
{
id: '@timestamp',
name: '@timestamp',
meta: {
type: 'date',
},
},
{
id: 'agent.keyword',
name: 'agent.keyword',
meta: {
type: 'string',
},
},
{
id: 'bytes',
name: 'bytes',
meta: {
type: 'number',
},
},
]);
expect(result).toMatchInlineSnapshot(`
Object {
"@timestamp": "date",
"agent.keyword": "string",
"bytes": "number",
}
`);
});
});
});

View file

@ -0,0 +1,24 @@
/*
* 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 { DatatableColumn, DatatableColumnType } from '@kbn/expressions-plugin/common';
type TextBasedColumnTypes = Record<string, DatatableColumnType>;
/**
* Column types for text based searches
* @param textBasedColumns
*/
export const getTextBasedColumnTypes = (
textBasedColumns: DatatableColumn[]
): TextBasedColumnTypes => {
return textBasedColumns.reduce<TextBasedColumnTypes>((map, next) => {
map[next.name] = next.meta.type;
return map;
}, {});
};

View file

@ -105,15 +105,15 @@ const build = (hit: EsHitRecord) => buildDataTableRecord(hit, dataViewMock);
describe('Unified data table cell rendering', function () {
it('renders bytes column correctly', () => {
const DataTableCellValue = getRenderCellValueFn(
dataViewMock,
rowsSource.map(build),
false,
() => false,
jest.fn(),
mockServices.fieldFormats as unknown as FieldFormatsStart,
100
);
const DataTableCellValue = getRenderCellValueFn({
dataView: dataViewMock,
rows: rowsSource.map(build),
useNewFieldsApi: false,
shouldShowFieldHandler: () => false,
closePopover: jest.fn(),
fieldFormats: mockServices.fieldFormats as unknown as FieldFormatsStart,
maxEntries: 100,
});
const component = shallow(
<DataTableCellValue
rowIndex={0}
@ -131,15 +131,15 @@ describe('Unified data table cell rendering', function () {
});
it('renders bytes column correctly using _source when details is true', () => {
const DataTableCellValue = getRenderCellValueFn(
dataViewMock,
rowsSource.map(build),
false,
() => false,
jest.fn(),
mockServices.fieldFormats as unknown as FieldFormatsStart,
100
);
const DataTableCellValue = getRenderCellValueFn({
dataView: dataViewMock,
rows: rowsSource.map(build),
useNewFieldsApi: false,
shouldShowFieldHandler: () => false,
closePopover: jest.fn(),
fieldFormats: mockServices.fieldFormats as unknown as FieldFormatsStart,
maxEntries: 100,
});
const component = shallow(
<DataTableCellValue
rowIndex={0}
@ -158,15 +158,15 @@ describe('Unified data table cell rendering', function () {
it('renders bytes column correctly using fields when details is true', () => {
const closePopoverMockFn = jest.fn();
const DataTableCellValue = getRenderCellValueFn(
dataViewMock,
rowsFields.map(build),
false,
() => false,
closePopoverMockFn,
mockServices.fieldFormats as unknown as FieldFormatsStart,
100
);
const DataTableCellValue = getRenderCellValueFn({
dataView: dataViewMock,
rows: rowsFields.map(build),
useNewFieldsApi: false,
shouldShowFieldHandler: () => false,
closePopover: closePopoverMockFn,
fieldFormats: mockServices.fieldFormats as unknown as FieldFormatsStart,
maxEntries: 100,
});
const component = mountWithIntl(
<DataTableCellValue
rowIndex={0}
@ -186,15 +186,15 @@ describe('Unified data table cell rendering', function () {
});
it('renders _source column correctly', () => {
const DataTableCellValue = getRenderCellValueFn(
dataViewMock,
rowsSource.map(build),
false,
(fieldName) => ['extension', 'bytes'].includes(fieldName),
jest.fn(),
mockServices.fieldFormats as unknown as FieldFormatsStart,
100
);
const DataTableCellValue = getRenderCellValueFn({
dataView: dataViewMock,
rows: rowsSource.map(build),
useNewFieldsApi: false,
shouldShowFieldHandler: (fieldName) => ['extension', 'bytes'].includes(fieldName),
closePopover: jest.fn(),
fieldFormats: mockServices.fieldFormats as unknown as FieldFormatsStart,
maxEntries: 100,
});
const component = shallow(
<DataTableCellValue
rowIndex={0}
@ -269,15 +269,15 @@ describe('Unified data table cell rendering', function () {
});
it('renders _source column correctly when isDetails is set to true', () => {
const DataTableCellValue = getRenderCellValueFn(
dataViewMock,
rowsSource.map(build),
false,
() => false,
jest.fn(),
mockServices.fieldFormats as unknown as FieldFormatsStart,
100
);
const DataTableCellValue = getRenderCellValueFn({
dataView: dataViewMock,
rows: rowsSource.map(build),
useNewFieldsApi: false,
shouldShowFieldHandler: () => false,
closePopover: jest.fn(),
fieldFormats: mockServices.fieldFormats as unknown as FieldFormatsStart,
maxEntries: 100,
});
const component = shallow(
<DataTableCellValue
rowIndex={0}
@ -345,15 +345,15 @@ describe('Unified data table cell rendering', function () {
});
it('renders fields-based column correctly', () => {
const DataTableCellValue = getRenderCellValueFn(
dataViewMock,
rowsFields.map(build),
true,
(fieldName) => ['extension', 'bytes'].includes(fieldName),
jest.fn(),
mockServices.fieldFormats as unknown as FieldFormatsStart,
100
);
const DataTableCellValue = getRenderCellValueFn({
dataView: dataViewMock,
rows: rowsFields.map(build),
useNewFieldsApi: true,
shouldShowFieldHandler: (fieldName) => ['extension', 'bytes'].includes(fieldName),
closePopover: jest.fn(),
fieldFormats: mockServices.fieldFormats as unknown as FieldFormatsStart,
maxEntries: 100,
});
const component = shallow(
<DataTableCellValue
rowIndex={0}
@ -432,16 +432,16 @@ describe('Unified data table cell rendering', function () {
});
it('limits amount of rendered items', () => {
const DataTableCellValue = getRenderCellValueFn(
dataViewMock,
rowsFields.map(build),
true,
(fieldName) => ['extension', 'bytes'].includes(fieldName),
jest.fn(),
mockServices.fieldFormats as unknown as FieldFormatsStart,
const DataTableCellValue = getRenderCellValueFn({
dataView: dataViewMock,
rows: rowsFields.map(build),
useNewFieldsApi: true,
shouldShowFieldHandler: (fieldName) => ['extension', 'bytes'].includes(fieldName),
closePopover: jest.fn(),
fieldFormats: mockServices.fieldFormats as unknown as FieldFormatsStart,
// this is the number of rendered items
1
);
maxEntries: 1,
});
const component = shallow(
<DataTableCellValue
rowIndex={0}
@ -477,41 +477,13 @@ describe('Unified data table cell rendering', function () {
<EuiDescriptionListTitle
className="unifiedDataTable__descriptionListTitle"
>
bytesDisplayName
and 3 more fields
</EuiDescriptionListTitle>
<EuiDescriptionListDescription
className="unifiedDataTable__descriptionListDescription"
dangerouslySetInnerHTML={
Object {
"__html": Array [
100,
],
}
}
/>
<EuiDescriptionListTitle
className="unifiedDataTable__descriptionListTitle"
>
_index
</EuiDescriptionListTitle>
<EuiDescriptionListDescription
className="unifiedDataTable__descriptionListDescription"
dangerouslySetInnerHTML={
Object {
"__html": "test",
}
}
/>
<EuiDescriptionListTitle
className="unifiedDataTable__descriptionListTitle"
>
_score
</EuiDescriptionListTitle>
<EuiDescriptionListDescription
className="unifiedDataTable__descriptionListDescription"
dangerouslySetInnerHTML={
Object {
"__html": 1,
"__html": "",
}
}
/>
@ -520,15 +492,15 @@ describe('Unified data table cell rendering', function () {
});
it('renders fields-based column correctly when isDetails is set to true', () => {
const DataTableCellValue = getRenderCellValueFn(
dataViewMock,
rowsFields.map(build),
true,
(fieldName) => false,
jest.fn(),
mockServices.fieldFormats as unknown as FieldFormatsStart,
100
);
const DataTableCellValue = getRenderCellValueFn({
dataView: dataViewMock,
rows: rowsFields.map(build),
useNewFieldsApi: true,
shouldShowFieldHandler: (fieldName) => false,
closePopover: jest.fn(),
fieldFormats: mockServices.fieldFormats as unknown as FieldFormatsStart,
maxEntries: 100,
});
const component = shallow(
<DataTableCellValue
rowIndex={0}
@ -601,15 +573,16 @@ describe('Unified data table cell rendering', function () {
});
it('collect object fields and renders them like _source', () => {
const DataTableCellValue = getRenderCellValueFn(
dataViewMock,
rowsFieldsWithTopLevelObject.map(build),
true,
(fieldName) => ['object.value', 'extension', 'bytes'].includes(fieldName),
jest.fn(),
mockServices.fieldFormats as unknown as FieldFormatsStart,
100
);
const DataTableCellValue = getRenderCellValueFn({
dataView: dataViewMock,
rows: rowsFieldsWithTopLevelObject.map(build),
useNewFieldsApi: true,
shouldShowFieldHandler: (fieldName) =>
['object.value', 'extension', 'bytes'].includes(fieldName),
closePopover: jest.fn(),
fieldFormats: mockServices.fieldFormats as unknown as FieldFormatsStart,
maxEntries: 100,
});
const component = shallow(
<DataTableCellValue
rowIndex={0}
@ -646,15 +619,16 @@ describe('Unified data table cell rendering', function () {
it('collect object fields and renders them like _source with fallback for unmapped', () => {
(dataViewMock.getFieldByName as jest.Mock).mockReturnValueOnce(undefined);
const DataTableCellValue = getRenderCellValueFn(
dataViewMock,
rowsFieldsWithTopLevelObject.map(build),
true,
(fieldName) => ['extension', 'bytes', 'object.value'].includes(fieldName),
jest.fn(),
mockServices.fieldFormats as unknown as FieldFormatsStart,
100
);
const DataTableCellValue = getRenderCellValueFn({
dataView: dataViewMock,
rows: rowsFieldsWithTopLevelObject.map(build),
useNewFieldsApi: true,
shouldShowFieldHandler: (fieldName) =>
['extension', 'bytes', 'object.value'].includes(fieldName),
closePopover: jest.fn(),
fieldFormats: mockServices.fieldFormats as unknown as FieldFormatsStart,
maxEntries: 100,
});
const component = shallow(
<DataTableCellValue
rowIndex={0}
@ -691,15 +665,15 @@ describe('Unified data table cell rendering', function () {
it('collect object fields and renders them as json in details', () => {
const closePopoverMockFn = jest.fn();
const DataTableCellValue = getRenderCellValueFn(
dataViewMock,
rowsFieldsWithTopLevelObject.map(build),
true,
() => false,
closePopoverMockFn,
mockServices.fieldFormats as unknown as FieldFormatsStart,
100
);
const DataTableCellValue = getRenderCellValueFn({
dataView: dataViewMock,
rows: rowsFieldsWithTopLevelObject.map(build),
useNewFieldsApi: true,
shouldShowFieldHandler: () => false,
closePopover: closePopoverMockFn,
fieldFormats: mockServices.fieldFormats as unknown as FieldFormatsStart,
maxEntries: 100,
});
const component = shallow(
<DataTableCellValue
rowIndex={0}
@ -759,15 +733,15 @@ describe('Unified data table cell rendering', function () {
it('renders a functional close button when CodeEditor is rendered', () => {
const closePopoverMockFn = jest.fn();
const DataTableCellValue = getRenderCellValueFn(
dataViewMock,
rowsFieldsWithTopLevelObject.map(build),
true,
() => false,
closePopoverMockFn,
mockServices.fieldFormats as unknown as FieldFormatsStart,
100
);
const DataTableCellValue = getRenderCellValueFn({
dataView: dataViewMock,
rows: rowsFieldsWithTopLevelObject.map(build),
useNewFieldsApi: true,
shouldShowFieldHandler: () => false,
closePopover: closePopoverMockFn,
fieldFormats: mockServices.fieldFormats as unknown as FieldFormatsStart,
maxEntries: 100,
});
const component = mountWithIntl(
<KibanaContextProvider services={mockServices}>
<DataTableCellValue
@ -788,15 +762,15 @@ describe('Unified data table cell rendering', function () {
it('does not collect subfields when the the column is unmapped but part of fields response', () => {
(dataViewMock.getFieldByName as jest.Mock).mockReturnValueOnce(undefined);
const DataTableCellValue = getRenderCellValueFn(
dataViewMock,
rowsFieldsWithTopLevelObject.map(build),
true,
() => false,
jest.fn(),
mockServices.fieldFormats as unknown as FieldFormatsStart,
100
);
const DataTableCellValue = getRenderCellValueFn({
dataView: dataViewMock,
rows: rowsFieldsWithTopLevelObject.map(build),
useNewFieldsApi: true,
shouldShowFieldHandler: () => false,
closePopover: jest.fn(),
fieldFormats: mockServices.fieldFormats as unknown as FieldFormatsStart,
maxEntries: 100,
});
const component = shallow(
<DataTableCellValue
rowIndex={0}
@ -823,15 +797,15 @@ describe('Unified data table cell rendering', function () {
});
it('renders correctly when invalid row is given', () => {
const DataTableCellValue = getRenderCellValueFn(
dataViewMock,
rowsSource.map(build),
false,
() => false,
jest.fn(),
mockServices.fieldFormats as unknown as FieldFormatsStart,
100
);
const DataTableCellValue = getRenderCellValueFn({
dataView: dataViewMock,
rows: rowsSource.map(build),
useNewFieldsApi: false,
shouldShowFieldHandler: () => false,
closePopover: jest.fn(),
fieldFormats: mockServices.fieldFormats as unknown as FieldFormatsStart,
maxEntries: 100,
});
const component = shallow(
<DataTableCellValue
rowIndex={1}
@ -849,15 +823,15 @@ describe('Unified data table cell rendering', function () {
});
it('renders correctly when invalid column is given', () => {
const DataTableCellValue = getRenderCellValueFn(
dataViewMock,
rowsSource.map(build),
false,
() => false,
jest.fn(),
mockServices.fieldFormats as unknown as FieldFormatsStart,
100
);
const DataTableCellValue = getRenderCellValueFn({
dataView: dataViewMock,
rows: rowsSource.map(build),
useNewFieldsApi: false,
shouldShowFieldHandler: () => false,
closePopover: jest.fn(),
fieldFormats: mockServices.fieldFormats as unknown as FieldFormatsStart,
maxEntries: 100,
});
const component = shallow(
<DataTableCellValue
rowIndex={0}
@ -888,15 +862,15 @@ describe('Unified data table cell rendering', function () {
},
},
];
const DataTableCellValue = getRenderCellValueFn(
dataViewMock,
rowsFieldsUnmapped.map(build),
true,
(fieldName) => ['unmapped'].includes(fieldName),
jest.fn(),
mockServices.fieldFormats as unknown as FieldFormatsStart,
100
);
const DataTableCellValue = getRenderCellValueFn({
dataView: dataViewMock,
rows: rowsFieldsUnmapped.map(build),
useNewFieldsApi: true,
shouldShowFieldHandler: (fieldName) => ['unmapped'].includes(fieldName),
closePopover: jest.fn(),
fieldFormats: mockServices.fieldFormats as unknown as FieldFormatsStart,
maxEntries: 100,
});
const component = shallow(
<DataTableCellValue
rowIndex={0}

View file

@ -25,6 +25,7 @@ import type {
DataTableRecord,
EsHitRecord,
ShouldShowFieldInTableHandler,
FormattedHit,
} from '@kbn/discover-utils/types';
import { formatFieldValue, formatHit } from '@kbn/discover-utils';
import { UnifiedDataTableContext } from '../table_context';
@ -33,21 +34,29 @@ import JsonCodeEditor from '../components/json_code_editor/json_code_editor';
const CELL_CLASS = 'unifiedDataTable__cellValue';
export const getRenderCellValueFn =
(
dataView: DataView,
rows: DataTableRecord[] | undefined,
useNewFieldsApi: boolean,
shouldShowFieldHandler: ShouldShowFieldInTableHandler,
closePopover: () => void,
fieldFormats: FieldFormatsStart,
maxEntries: number,
externalCustomRenderers?: Record<
string,
(props: EuiDataGridCellValueElementProps) => React.ReactNode
>
) =>
({
export const getRenderCellValueFn = ({
dataView,
rows,
useNewFieldsApi,
shouldShowFieldHandler,
closePopover,
fieldFormats,
maxEntries,
externalCustomRenderers,
}: {
dataView: DataView;
rows: DataTableRecord[] | undefined;
useNewFieldsApi: boolean;
shouldShowFieldHandler: ShouldShowFieldInTableHandler;
closePopover: () => void;
fieldFormats: FieldFormatsStart;
maxEntries: number;
externalCustomRenderers?: Record<
string,
(props: EuiDataGridCellValueElementProps) => React.ReactNode
>;
}) => {
return ({
rowIndex,
columnId,
isDetails,
@ -122,7 +131,7 @@ export const getRenderCellValueFn =
}
if (field?.type === '_source' || useTopLevelObjectColumns) {
const pairs = useTopLevelObjectColumns
const pairs: FormattedHit = useTopLevelObjectColumns
? getTopLevelObjectPairs(row.raw, columnId, dataView, shouldShowFieldHandler).slice(
0,
maxEntries
@ -135,10 +144,10 @@ export const getRenderCellValueFn =
compressed
className={classnames('unifiedDataTable__descriptionList', CELL_CLASS)}
>
{pairs.map(([key, value]) => (
<Fragment key={key}>
{pairs.map(([fieldDisplayName, value]) => (
<Fragment key={fieldDisplayName}>
<EuiDescriptionListTitle className="unifiedDataTable__descriptionListTitle">
{key}
{fieldDisplayName}
</EuiDescriptionListTitle>
<EuiDescriptionListDescription
className="unifiedDataTable__descriptionListDescription"
@ -161,6 +170,7 @@ export const getRenderCellValueFn =
/>
);
};
};
/**
* Helper function to show top level objects
@ -272,8 +282,8 @@ function getTopLevelObjectPairs(
const innerColumns = getInnerColumns(row.fields as Record<string, unknown[]>, columnId);
// Put the most important fields first
const highlights: Record<string, unknown> = (row.highlight as Record<string, unknown>) ?? {};
const highlightPairs: Array<[string, string]> = [];
const sourcePairs: Array<[string, string]> = [];
const highlightPairs: FormattedHit = [];
const sourcePairs: FormattedHit = [];
Object.entries(innerColumns).forEach(([key, values]) => {
const subField = dataView.getFieldByName(key);
const displayKey = dataView.fields.getByName
@ -293,10 +303,10 @@ function getTopLevelObjectPairs(
const pairs = highlights[key] ? highlightPairs : sourcePairs;
if (displayKey) {
if (shouldShowFieldHandler(displayKey)) {
pairs.push([displayKey, formatted]);
pairs.push([displayKey, formatted, key]);
}
} else {
pairs.push([key, formatted]);
pairs.push([key, formatted, key]);
}
});
return [...highlightPairs, ...sourcePairs];

View file

@ -33,5 +33,6 @@
"@kbn/code-editor",
"@kbn/config",
"@kbn/monaco",
"@kbn/field-utils",
]
}

View file

@ -14,7 +14,7 @@ import { i18n } from '@kbn/i18n';
import { FieldIcon, FieldIconProps } from '@kbn/react-field';
import type { DataViewField } from '@kbn/data-views-plugin/public';
import { getDataViewFieldSubtypeMulti } from '@kbn/es-query';
import { getFieldTypeName } from '@kbn/discover-utils';
import { getFieldTypeName } from '@kbn/field-utils';
interface Props {
fieldName: string;

View file

@ -29,6 +29,11 @@ export interface DocViewRenderProps {
hit: DataTableRecord;
dataView: DataView;
columns?: string[];
/**
* If not provided, types will be derived by default from the dataView field types.
* For displaying text-based search results, define column types (which are available separately in the fetch request) here.
*/
columnTypes?: Record<string, string>;
query?: Query | AggregateQuery;
textBasedHits?: DataTableRecord[];
filter?: DocViewFilterFn;

View file

@ -23,5 +23,6 @@
"@kbn/i18n-react",
"@kbn/i18n",
"@kbn/react-field",
"@kbn/field-utils",
]
}

View file

@ -9,7 +9,6 @@
export { FieldList, type FieldListProps } from './src/components/field_list';
export { FieldListGrouped, type FieldListGroupedProps } from './src/components/field_list_grouped';
export { FieldListFilters, type FieldListFiltersProps } from './src/components/field_list_filters';
export { FieldIcon, type FieldIconProps, getFieldIconProps } from './src/components/field_icon';
export { FieldItemButton, type FieldItemButtonProps } from './src/components/field_item_button';
export type {
FieldTopValuesBucketProps,
@ -85,8 +84,6 @@ export {
type QuerySubscriberParams,
} from './src/hooks/use_query_subscriber';
export { getFieldTypeDescription, getFieldType, getFieldIconType } from './src/utils/field_types';
export {
UnifiedFieldListSidebarContainer,
type UnifiedFieldListSidebarContainerApi,

View file

@ -12,8 +12,8 @@ import classnames from 'classnames';
import { FieldButton, type FieldButtonProps } from '@kbn/react-field';
import { EuiButtonIcon, EuiButtonIconProps, EuiHighlight, EuiIcon, EuiToolTip } from '@elastic/eui';
import type { DataViewField } from '@kbn/data-views-plugin/common';
import { FieldIcon, getFieldIconProps } from '@kbn/field-utils';
import { type FieldListItem, type GetCustomFieldType } from '../../types';
import { FieldIcon, getFieldIconProps } from '../field_icon';
import { fieldNameWildcardMatcher } from '../../utils/field_name_wildcard_matcher';
import './field_item_button.scss';

View file

@ -32,10 +32,15 @@ import type { CoreStart } from '@kbn/core-lifecycle-browser';
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import { type DataViewField } from '@kbn/data-views-plugin/common';
import type { FieldTypeKnown } from '@kbn/discover-utils/types';
import { getFieldTypeName, isKnownFieldType, KNOWN_FIELD_TYPE_LIST } from '@kbn/discover-utils';
import { FieldIcon } from '../field_icon';
import { getFieldIconType, getFieldTypeDescription } from '../../utils/field_types';
import {
type FieldTypeKnown,
getFieldIconType,
getFieldTypeDescription,
getFieldTypeName,
isKnownFieldType,
KNOWN_FIELD_TYPE_LIST,
FieldIcon,
} from '@kbn/field-utils';
import type { FieldListItem, GetCustomFieldType } from '../../types';
const EQUAL_HEIGHT_OFFSET = 2; // to avoid changes in the header's height after "Clear all" button appears

View file

@ -10,10 +10,9 @@ import { useMemo, useState } from 'react';
import { htmlIdGenerator } from '@elastic/eui';
import { type DataViewField } from '@kbn/data-views-plugin/common';
import type { CoreStart } from '@kbn/core-lifecycle-browser';
import type { FieldTypeKnown } from '@kbn/discover-utils/types';
import { type FieldTypeKnown, getFieldIconType } from '@kbn/field-utils';
import { type FieldListFiltersProps } from '../components/field_list_filters';
import { type FieldListItem, GetCustomFieldType } from '../types';
import { getFieldIconType } from '../utils/field_types';
import { fieldNameWildcardMatcher } from '../utils/field_name_wildcard_matcher';
const htmlId = htmlIdGenerator('fieldList');

View file

@ -8,7 +8,7 @@
import type { DataViewField } from '@kbn/data-views-plugin/common';
import type { EuiButtonIconProps, EuiButtonProps } from '@elastic/eui';
import type { FieldTypeKnown } from '@kbn/discover-utils/types';
import type { FieldTypeKnown, FieldBase } from '@kbn/field-utils/types';
export interface BucketedAggregation<KeyType = string> {
buckets: Array<{
@ -48,15 +48,7 @@ export enum ExistenceFetchStatus {
unknown = 'unknown',
}
export interface FieldListItem {
name: DataViewField['name'];
type?: DataViewField['type'];
displayName?: DataViewField['displayName'];
count?: DataViewField['count'];
timeSeriesMetric?: DataViewField['timeSeriesMetric'];
esTypes?: DataViewField['esTypes'];
scripted?: DataViewField['scripted'];
}
export type FieldListItem = FieldBase;
export enum FieldsGroupNames {
SpecialFields = 'SpecialFields',

View file

@ -30,6 +30,7 @@
"@kbn/discover-utils",
"@kbn/ebt-tools",
"@kbn/shared-ux-button-toolbar",
"@kbn/field-utils",
],
"exclude": ["target/**/*"]
}

View file

@ -11,7 +11,7 @@ import React, { useRef } from 'react';
import { i18n } from '@kbn/i18n';
import useEffectOnce from 'react-use/lib/useEffectOnce';
import { EuiComboBox, EuiComboBoxProps, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { FieldIcon } from '@kbn/unified-field-list/src/components/field_icon';
import { FieldIcon } from '@kbn/field-utils/src/components/field_icon';
import classNames from 'classnames';
import type { DataType } from './types';
import { TruncatedLabel } from './truncated_label';

View file

@ -30,6 +30,6 @@
"@kbn/ui-theme",
"@kbn/coloring",
"@kbn/field-formats-plugin",
"@kbn/unified-field-list",
"@kbn/field-utils",
],
}

View file

@ -191,6 +191,7 @@ export function ContextAppContent({
<CellActionsProvider getTriggerCompatibleActions={uiActions.getTriggerCompatibleActions}>
<DiscoverGridMemoized
ariaLabelledBy="surDocumentsAriaLabel"
showColumnTokens
columns={columns}
rows={rows}
dataView={dataView}

View file

@ -21,7 +21,13 @@ import { SortOrder } from '@kbn/saved-search-plugin/public';
import { CellActionsProvider } from '@kbn/cell-actions';
import type { DataTableRecord } from '@kbn/discover-utils/types';
import { SearchResponseWarnings } from '@kbn/search-response-warnings';
import { DataLoadingState, UnifiedDataTable, useColumns } from '@kbn/unified-data-table';
import {
DataLoadingState,
UnifiedDataTable,
useColumns,
type DataTableColumnTypes,
getTextBasedColumnTypes,
} from '@kbn/unified-data-table';
import {
DOC_HIDE_TIME_COLUMN_SETTING,
DOC_TABLE_LEGACY,
@ -62,8 +68,10 @@ const progressStyle = css`
z-index: 2;
`;
const TOUR_STEPS = { expandButton: DISCOVER_TOUR_STEP_ANCHOR_IDS.expandDocument };
const DocTableInfiniteMemoized = React.memo(DocTableInfinite);
const DataGridMemoized = React.memo(UnifiedDataTable);
const DiscoverGridMemoized = React.memo(UnifiedDataTable);
// export needs for testing
export const onResize = (
@ -198,14 +206,28 @@ function DiscoverDocumentsComponent({
[isTextBasedQuery, columns, uiSettings, dataView.timeFieldName]
);
const columnTypes: DataTableColumnTypes | undefined = useMemo(
() =>
documentState.textBasedQueryColumns
? getTextBasedColumnTypes(documentState.textBasedQueryColumns)
: undefined,
[documentState.textBasedQueryColumns]
);
const renderDocumentView = useCallback(
(hit: DataTableRecord, displayedRows: DataTableRecord[], displayedColumns: string[]) => (
(
hit: DataTableRecord,
displayedRows: DataTableRecord[],
displayedColumns: string[],
customColumnTypes?: DataTableColumnTypes
) => (
<DiscoverGridFlyout
dataView={dataView}
hit={hit}
hits={displayedRows}
// if default columns are used, dont make them part of the URL - the context state handling will take care to restore them
columns={displayedColumns}
columnTypes={customColumnTypes}
savedSearchId={savedSearch.id}
onFilter={onAddFilter}
onRemoveColumn={onRemoveColumn}
@ -277,9 +299,11 @@ function DiscoverDocumentsComponent({
<CellActionsProvider
getTriggerCompatibleActions={uiActions.getTriggerCompatibleActions}
>
<DataGridMemoized
<DiscoverGridMemoized
ariaLabelledBy="documentsAriaLabel"
showColumnTokens
columns={currentColumns}
columnTypes={columnTypes}
expandedDoc={expandedDoc}
dataView={dataView}
loadingState={
@ -316,7 +340,7 @@ function DiscoverDocumentsComponent({
services={services}
totalHits={totalHits}
onFetchMoreRecords={onFetchMoreRecords}
componentsTourSteps={{ expandButton: DISCOVER_TOUR_STEP_ANCHOR_IDS.expandDocument }}
componentsTourSteps={TOUR_STEPS}
/>
</CellActionsProvider>
</div>

View file

@ -34,6 +34,7 @@ export interface DiscoverGridFlyoutProps {
filters?: Filter[];
query?: Query | AggregateQuery;
columns: string[];
columnTypes?: Record<string, string>;
hit: DataTableRecord;
hits?: DataTableRecord[];
dataView: DataView;
@ -57,6 +58,7 @@ export function DiscoverGridFlyout({
hits,
dataView,
columns,
columnTypes,
savedSearchId,
filters,
query,
@ -160,6 +162,7 @@ export function DiscoverGridFlyout({
<UnifiedDocViewer
hit={actualHit}
columns={columns}
columnTypes={columnTypes}
dataView={dataView}
filter={onFilter}
onRemoveColumn={(columnName: string) => {

View file

@ -68,26 +68,32 @@ describe('Row formatter', () => {
Array [
"also",
"with \\"quotes\\" or 'single quotes'",
"also",
],
Array [
"foo",
"bar",
"foo",
],
Array [
"hello",
"<h1>World</h1>",
"hello",
],
Array [
"number",
42,
"number",
],
Array [
"_id",
"a",
"_id",
],
Array [
"_score",
1,
"_score",
],
]
}
@ -112,26 +118,12 @@ describe('Row formatter', () => {
Array [
"also",
"with \\"quotes\\" or 'single quotes'",
"also",
],
Array [
"foo",
"bar",
],
Array [
"hello",
"<h1>World</h1>",
],
Array [
"number",
42,
],
Array [
"_id",
"a",
],
Array [
"_score",
1,
"and 4 more fields",
"",
null,
],
]
}
@ -153,26 +145,32 @@ describe('Row formatter', () => {
Array [
"number",
42,
"number",
],
Array [
"also",
"with \\"quotes\\" or 'single quotes'",
"also",
],
Array [
"foo",
"bar",
"foo",
],
Array [
"hello",
"<h1>World</h1>",
"hello",
],
Array [
"_id",
"a",
"_id",
],
Array [
"_score",
1,
"_score",
],
]
}
@ -208,6 +206,7 @@ describe('Row formatter', () => {
Array [
"object.value",
"formatted, formatted",
"object.value",
],
]
}
@ -266,10 +265,12 @@ describe('Row formatter', () => {
Array [
"object.keys",
"formatted, formatted",
"object.keys",
],
Array [
"object.value",
"formatted, formatted",
"object.value",
],
]
}
@ -301,6 +302,7 @@ describe('Row formatter', () => {
Array [
"object.value",
"5, 10",
"object.value",
],
]
}

View file

@ -9,13 +9,17 @@
import React, { Fragment } from 'react';
import type { DataView } from '@kbn/data-views-plugin/public';
import { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
import type { DataTableRecord, ShouldShowFieldInTableHandler } from '@kbn/discover-utils/types';
import type {
DataTableRecord,
ShouldShowFieldInTableHandler,
FormattedHit,
} from '@kbn/discover-utils/types';
import { formatHit } from '@kbn/discover-utils';
import './row_formatter.scss';
interface Props {
defPairs: Array<readonly [string, string]>;
defPairs: FormattedHit;
}
const TemplateComponent = ({ defPairs }: Props) => {
return (
@ -57,8 +61,8 @@ export const formatTopLevelObject = (
maxEntries: number
) => {
const highlights = row.highlight ?? {};
const highlightPairs: Array<[string, string]> = [];
const sourcePairs: Array<[string, string]> = [];
const highlightPairs: FormattedHit = [];
const sourcePairs: FormattedHit = [];
const sorted = Object.entries(fields).sort(([keyA], [keyB]) => keyA.localeCompare(keyB));
sorted.forEach(([key, values]) => {
const field = dataView.getFieldByName(key);
@ -76,7 +80,7 @@ export const formatTopLevelObject = (
)
.join(', ');
const pairs = highlights[key] ? highlightPairs : sourcePairs;
pairs.push([displayKey ? displayKey : key, formatted]);
pairs.push([displayKey ? displayKey : key, formatted, key]);
});
return <TemplateComponent defPairs={[...highlightPairs, ...sourcePairs].slice(0, maxEntries)} />;
};

View file

@ -59,9 +59,8 @@ import {
SORT_DEFAULT_ORDER_SETTING,
buildDataTableRecord,
} from '@kbn/discover-utils';
import type { UnifiedDataTableProps } from '@kbn/unified-data-table';
import type { UnifiedDataTableSettings } from '@kbn/unified-data-table';
import { columnActions } from '@kbn/unified-data-table';
import type { UnifiedDataTableProps, UnifiedDataTableSettings } from '@kbn/unified-data-table';
import { columnActions, getTextBasedColumnTypes } from '@kbn/unified-data-table';
import { VIEW_MODE, getDefaultRowsPerPage } from '../../common/constants';
import type { ISearchEmbeddable, SearchInput, SearchOutput } from './types';
import type { DiscoverServices } from '../build_services';
@ -341,6 +340,9 @@ export class SavedSearchEmbeddable
loading: false,
});
searchProps.columnTypes = result.textBasedQueryColumns
? getTextBasedColumnTypes(result.textBasedQueryColumns)
: undefined;
searchProps.rows = result.records;
searchProps.totalHitCount = result.records.length;
searchProps.isLoading = false;

View file

@ -43,6 +43,7 @@ export function SavedSearchEmbeddableComponent({
{...(searchProps as DiscoverGridEmbeddableProps)} // TODO later: remove the type casting to prevent unexpected errors due to missing props!
loadingState={searchProps.isLoading ? DataLoadingState.loading : DataLoadingState.loaded}
showFullScreenButton={false}
showColumnTokens
query={query}
className="unifiedDataTable"
/>

View file

@ -5,13 +5,14 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import React, { memo, useCallback, useState } from 'react';
import React, { useCallback, useState } from 'react';
import type { DataTableRecord } from '@kbn/discover-utils/types';
import { AggregateQuery, Query } from '@kbn/es-query';
import type { SearchResponseInterceptedWarning } from '@kbn/search-response-warnings';
import {
DataLoadingState as DiscoverGridLoadingState,
UnifiedDataTable,
type DataTableColumnTypes,
} from '@kbn/unified-data-table';
import type { UnifiedDataTableProps } from '@kbn/unified-data-table';
import './saved_search_grid.scss';
@ -29,20 +30,26 @@ export interface DiscoverGridEmbeddableProps extends UnifiedDataTableProps {
savedSearchId?: string;
}
export const DataGridMemoized = memo(UnifiedDataTable);
export const DiscoverGridMemoized = React.memo(UnifiedDataTable);
export function DiscoverGridEmbeddable(props: DiscoverGridEmbeddableProps) {
const { interceptedWarnings, ...gridProps } = props;
const [expandedDoc, setExpandedDoc] = useState<DataTableRecord | undefined>(undefined);
const renderDocumentView = useCallback(
(hit: DataTableRecord, displayedRows: DataTableRecord[], displayedColumns: string[]) => (
(
hit: DataTableRecord,
displayedRows: DataTableRecord[],
displayedColumns: string[],
customColumnTypes?: DataTableColumnTypes
) => (
<DiscoverGridFlyout
dataView={props.dataView}
hit={hit}
hits={displayedRows}
// if default columns are used, dont make them part of the URL - the context state handling will take care to restore them
columns={displayedColumns}
columnTypes={customColumnTypes}
savedSearchId={props.savedSearchId}
onFilter={props.onFilter}
onRemoveColumn={props.onRemoveColumn}
@ -69,7 +76,7 @@ export function DiscoverGridEmbeddable(props: DiscoverGridEmbeddableProps) {
dataTestSubj="embeddedSavedSearchDocTable"
interceptedWarnings={props.interceptedWarnings}
>
<DataGridMemoized
<DiscoverGridMemoized
{...gridProps}
totalHits={props.totalHitCount}
setExpandedDoc={setExpandedDoc}

View file

@ -9,7 +9,7 @@
import '../table.scss';
import React, { useCallback, useMemo } from 'react';
import { EuiInMemoryTable } from '@elastic/eui';
import { getFieldIconType } from '@kbn/unified-field-list/src/utils/field_types/get_field_icon_type';
import { getFieldIconType } from '@kbn/field-utils/src/utils/get_field_icon_type';
import {
SHOW_MULTIFIELDS,
formatFieldValue,

View file

@ -29,7 +29,7 @@ import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { debounce } from 'lodash';
import { Storage } from '@kbn/kibana-utils-plugin/public';
import { getFieldIconType } from '@kbn/unified-field-list/src/utils/field_types/get_field_icon_type';
import { getFieldIconType } from '@kbn/field-utils/src/utils/get_field_icon_type';
import {
SHOW_MULTIFIELDS,
formatFieldValue,
@ -105,6 +105,7 @@ const updateSearchText = debounce(
export const DocViewerTable = ({
columns,
columnTypes,
hit,
dataView,
filter,
@ -165,7 +166,9 @@ export const DocViewerTable = ({
(field: string) => {
const fieldMapping = mapping(field);
const displayName = fieldMapping?.displayName ?? field;
const fieldType = isNestedFieldParent(field, dataView)
const fieldType = columnTypes
? columnTypes[field] // for text-based results types come separately
: isNestedFieldParent(field, dataView)
? 'nested'
: fieldMapping
? getFieldIconType(fieldMapping)
@ -208,6 +211,7 @@ export const DocViewerTable = ({
onToggleColumn,
filter,
columns,
columnTypes,
flattened,
pinnedFields,
onTogglePinned,

View file

@ -13,7 +13,6 @@
"@kbn/i18n-react",
"@kbn/i18n",
"@kbn/unified-doc-viewer",
"@kbn/unified-field-list",
"@kbn/kibana-utils-plugin",
"@kbn/data-plugin",
"@kbn/core-analytics-browser",
@ -23,7 +22,8 @@
"@kbn/core",
"@kbn/shared-ux-utility",
"@kbn/core-analytics-browser-mocks",
"@kbn/core-ui-settings-browser-mocks"
"@kbn/core-ui-settings-browser-mocks",
"@kbn/field-utils"
],
"exclude": [
"target/**/*",

View file

@ -0,0 +1,184 @@
/*
* 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 expect from '@kbn/expect';
import { WebElementWrapper } from '../../../services/lib/web_element_wrapper';
import { FtrProviderContext } from '../ftr_provider_context';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const dataGrid = getService('dataGrid');
const PageObjects = getPageObjects([
'common',
'discover',
'timePicker',
'dashboard',
'unifiedFieldList',
'header',
]);
const esArchiver = getService('esArchiver');
const dashboardAddPanel = getService('dashboardAddPanel');
const testSubjects = getService('testSubjects');
const kibanaServer = getService('kibanaServer');
const security = getService('security');
const defaultSettings = {
defaultIndex: 'logstash-*',
hideAnnouncements: true,
};
async function findFirstColumnTokens() {
const header = await testSubjects.find('euiDataGridBody > dataGridHeader');
return await findFirstFieldIcons(header);
}
async function findFirstDocViewerTokens() {
await dataGrid.clickRowToggle({ rowIndex: 0 });
const docViewer = await testSubjects.find('docTableDetailsFlyout');
return await findFirstFieldIcons(docViewer);
}
async function findFirstFieldIcons(element: WebElementWrapper) {
const fieldIcons = await element.findAllByCssSelector('.kbnFieldIcon svg');
return await Promise.all(
fieldIcons.map((fieldIcon) => fieldIcon.getAttribute('aria-label')).slice(0, 10)
);
}
describe('discover data grid field tokens', function () {
before(async () => {
await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader']);
await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional');
await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover');
});
after(async () => {
await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover');
await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional');
await kibanaServer.savedObjects.cleanStandardList();
});
beforeEach(async function () {
await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings();
await kibanaServer.uiSettings.update(defaultSettings);
await PageObjects.common.navigateToApp('discover');
await PageObjects.discover.waitUntilSearchingHasFinished();
});
it('should not render field tokens when Document column is visible', async function () {
expect(await PageObjects.discover.getHitCount()).to.be('14,004');
expect(await findFirstColumnTokens()).to.eql([]);
expect(await findFirstDocViewerTokens()).to.eql([
'Keyword',
'Keyword',
'Number',
'Text',
'Text',
'Date',
'Text',
'Number',
'IP address',
'Text',
]);
});
it('should render field tokens correctly when columns are selected', async function () {
await PageObjects.unifiedFieldList.clickFieldListItemAdd('bytes');
await PageObjects.unifiedFieldList.clickFieldListItemAdd('extension');
await PageObjects.unifiedFieldList.clickFieldListItemAdd('ip');
await PageObjects.unifiedFieldList.clickFieldListItemAdd('geo.coordinates');
expect(await findFirstColumnTokens()).to.eql(['Number', 'Text', 'IP address', 'Geo point']);
expect(await findFirstDocViewerTokens()).to.eql([
'Keyword',
'Keyword',
'Number',
'Text',
'Text',
'Date',
'Text',
'Number',
'IP address',
'Text',
]);
});
it('should render field tokens correctly for ES|QL', async function () {
await PageObjects.discover.selectTextBaseLang();
expect(await PageObjects.discover.getHitCount()).to.be('10');
await PageObjects.unifiedFieldList.clickFieldListItemAdd('@timestamp');
await PageObjects.unifiedFieldList.clickFieldListItemAdd('bytes');
await PageObjects.unifiedFieldList.clickFieldListItemAdd('extension');
await PageObjects.unifiedFieldList.clickFieldListItemAdd('ip');
await PageObjects.unifiedFieldList.clickFieldListItemAdd('geo.coordinates');
expect(await findFirstColumnTokens()).to.eql(['Number', 'String', 'String']);
expect(await findFirstDocViewerTokens()).to.eql([
'String',
'String',
'Date',
'String',
'Number',
'String',
'String',
'Unknown field',
'String',
'String',
]);
});
it('should render field tokens correctly on Dashboard', async function () {
await PageObjects.unifiedFieldList.clickFieldListItemAdd('bytes');
await PageObjects.unifiedFieldList.clickFieldListItemAdd('extension');
await PageObjects.unifiedFieldList.clickFieldListItemAdd('geo.coordinates');
await PageObjects.unifiedFieldList.clickFieldListItemAdd(
'relatedContent.article:modified_time'
);
await PageObjects.discover.saveSearch('With columns');
await PageObjects.common.navigateToApp('dashboard');
await PageObjects.dashboard.clickNewDashboard();
await dashboardAddPanel.clickOpenAddPanel();
await dashboardAddPanel.addSavedSearch('With columns');
expect(await findFirstColumnTokens()).to.eql(['Number', 'Text', 'Geo point', 'Date']);
expect(await findFirstDocViewerTokens()).to.eql([
'Keyword',
'Keyword',
'Number',
'Text',
'Text',
'Date',
'Text',
'Number',
'IP address',
'Text',
]);
});
it('should render field tokens correctly on Surrounding Documents page', async function () {
await PageObjects.unifiedFieldList.clickFieldListItemAdd('bytes');
await PageObjects.unifiedFieldList.clickFieldListItemAdd('extension');
// navigate to the context view
await dataGrid.clickRowToggle({ rowIndex: 0 });
const [, surroundingActionEl] = await dataGrid.getRowActions({
isAnchorRow: false,
rowIndex: 0,
});
await surroundingActionEl.click();
await PageObjects.header.waitUntilLoadingHasFinished();
expect(await findFirstColumnTokens()).to.eql(['Number', 'Text']);
});
});
}

View file

@ -32,6 +32,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./_data_grid_row_height'));
loadTestFile(require.resolve('./_data_grid_pagination'));
loadTestFile(require.resolve('./_data_grid_footer'));
loadTestFile(require.resolve('./_data_grid_field_tokens'));
loadTestFile(require.resolve('./_adhoc_data_views'));
loadTestFile(require.resolve('./_esql_view'));
loadTestFile(require.resolve('./_indexpattern_with_unmapped_fields'));

View file

@ -268,8 +268,8 @@ export class DataGridService extends FtrService {
const textArr = [];
for (const cell of result) {
const textContent = await cell.getAttribute('textContent');
textArr.push(textContent.trim());
const cellText = await cell.getVisibleText();
textArr.push(cellText.trim());
}
return Promise.resolve(textArr);
}

View file

@ -786,6 +786,8 @@
"@kbn/field-formats-plugin/*": ["src/plugins/field_formats/*"],
"@kbn/field-types": ["packages/kbn-field-types"],
"@kbn/field-types/*": ["packages/kbn-field-types/*"],
"@kbn/field-utils": ["packages/kbn-field-utils"],
"@kbn/field-utils/*": ["packages/kbn-field-utils/*"],
"@kbn/file-upload-plugin": ["x-pack/plugins/file_upload"],
"@kbn/file-upload-plugin/*": ["x-pack/plugins/file_upload/*"],
"@kbn/files-example-plugin": ["examples/files_example"],

View file

@ -9,7 +9,7 @@ import React, { FC } from 'react';
import { EuiToolTip } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FieldIcon } from '@kbn/react-field';
import { getFieldTypeName } from '@kbn/discover-utils';
import { getFieldTypeName } from '@kbn/field-utils';
import './_index.scss';
interface FieldTypeIconProps {

View file

@ -8,7 +8,7 @@
import React, { FC, useMemo } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { getFieldTypeName } from '@kbn/discover-utils';
import { getFieldTypeName } from '@kbn/field-utils';
import { FieldTypesHelpPopover } from './field_types_help_popover';
import { MultiSelectPicker, Option } from '../multi_select_picker';
import type {

View file

@ -22,7 +22,7 @@ import React, { FC, useMemo, useState } from 'react';
import { i18n } from '@kbn/i18n';
import { FieldIcon } from '@kbn/react-field';
import { FormattedMessage } from '@kbn/i18n-react';
import { getFieldTypeDescription } from '@kbn/unified-field-list/src/utils/field_types/get_field_type_description';
import { getFieldTypeDescription } from '@kbn/field-utils/src/utils/get_field_type_description';
import { useDataVisualizerKibana } from '../../../kibana_context';
interface FieldTypeTableItem {

View file

@ -7,7 +7,7 @@
import { DataViewField } from '@kbn/data-views-plugin/public';
import { KBN_FIELD_TYPES } from '@kbn/field-types';
import { getFieldType } from '@kbn/unified-field-list/src/utils/field_types/get_field_type';
import { getFieldType } from '@kbn/field-utils/src/utils/get_field_type';
import { SUPPORTED_FIELD_TYPES } from '../../../../common/constants';
// convert kibana types to ML Job types

View file

@ -9,7 +9,7 @@ import React, { FC, useMemo } from 'react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { css } from '@emotion/react';
import { getFieldTypeName } from '@kbn/discover-utils';
import { getFieldTypeName } from '@kbn/field-utils';
import { useCurrentEuiTheme } from '../../../common/hooks/use_current_eui_theme';
import { FieldTypesHelpPopover } from '../../../common/components/field_types_filter/field_types_help_popover';
import { FieldTypeIcon } from '../../../common/components/field_type_icon';

View file

@ -25,7 +25,6 @@
"@kbn/data-views-plugin",
"@kbn/datemath",
"@kbn/discover-plugin",
"@kbn/discover-utils",
"@kbn/embeddable-plugin",
"@kbn/embeddable-plugin",
"@kbn/es-query",
@ -63,13 +62,13 @@
"@kbn/unified-search-plugin",
"@kbn/usage-collection-plugin",
"@kbn/utility-types",
"@kbn/unified-field-list",
"@kbn/ml-string-hash",
"@kbn/ml-random-sampler-utils",
"@kbn/data-service",
"@kbn/core-notifications-browser",
"@kbn/ebt-tools",
"@kbn/ml-chi2test"
"@kbn/ml-chi2test",
"@kbn/field-utils"
],
"exclude": [
"target/**/*",

View file

@ -24,7 +24,8 @@ import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks';
import { DataView, DataViewField } from '@kbn/data-views-plugin/common';
import { loadFieldStats } from '@kbn/unified-field-list/src/services/field_stats';
import { DOCUMENT_FIELD_NAME } from '../../../common/constants';
import { FieldIcon, FieldStats, FieldPopoverFooter } from '@kbn/unified-field-list';
import { FieldIcon } from '@kbn/field-utils';
import { FieldStats, FieldPopoverFooter } from '@kbn/unified-field-list';
jest.mock('@kbn/unified-field-list/src/services/field_stats', () => ({
loadFieldStats: jest.fn().mockResolvedValue({}),

View file

@ -12,7 +12,7 @@ import { i18n } from '@kbn/i18n';
import { EuiComboBoxOptionOption, EuiComboBoxProps } from '@elastic/eui';
import { useExistingFieldsReader } from '@kbn/unified-field-list/src/hooks/use_existing_fields';
import { FieldOption, FieldOptionValue, FieldPicker } from '@kbn/visualization-ui-components';
import { getFieldIconType } from '@kbn/unified-field-list';
import { getFieldIconType } from '@kbn/field-utils';
import type { OperationType } from '../form_based';
import type { OperationSupportMatrix } from './operation_support';
import { fieldContainsData } from '../../../shared_components';

View file

@ -90,6 +90,7 @@
"@kbn/search-response-warnings",
"@kbn/logging",
"@kbn/core-plugins-server",
"@kbn/field-utils"
],
"exclude": [
"target/**/*",

View file

@ -2398,34 +2398,34 @@
"discover.viewAlert.searchSourceErrorTitle": "Erreur lors de la récupération de la source de recherche",
"discover.viewModes.document.label": "Documents",
"discover.viewModes.fieldStatistics.label": "Statistiques de champ",
"discover.fieldNameIcons.binaryAriaLabel": "Binaire",
"discover.fieldNameIcons.booleanAriaLabel": "Booléen",
"discover.fieldNameIcons.conflictFieldAriaLabel": "Conflit",
"discover.fieldNameIcons.counterFieldAriaLabel": "Indicateur de compteur",
"discover.fieldNameIcons.dateFieldAriaLabel": "Date",
"discover.fieldNameIcons.dateRangeFieldAriaLabel": "Plage de dates",
"discover.fieldNameIcons.denseVectorFieldAriaLabel": "Vecteur dense",
"discover.fieldNameIcons.flattenedFieldAriaLabel": "Lissé",
"discover.fieldNameIcons.gaugeFieldAriaLabel": "Indicateur de jauge",
"discover.fieldNameIcons.geoPointFieldAriaLabel": "Point géographique",
"discover.fieldNameIcons.geoShapeFieldAriaLabel": "Forme géométrique",
"discover.fieldNameIcons.histogramFieldAriaLabel": "Histogramme",
"discover.fieldNameIcons.ipAddressFieldAriaLabel": "Adresse IP",
"discover.fieldNameIcons.ipRangeFieldAriaLabel": "Plage d'IP",
"discover.fieldNameIcons.keywordFieldAriaLabel": "Mot-clé",
"discover.fieldNameIcons.murmur3FieldAriaLabel": "Murmur3",
"discover.fieldNameIcons.nestedFieldAriaLabel": "Imbriqué",
"discover.fieldNameIcons.numberFieldAriaLabel": "Nombre",
"discover.fieldNameIcons.pointFieldAriaLabel": "Point",
"discover.fieldNameIcons.rankFeatureFieldAriaLabel": "Fonctionnalité de rang",
"discover.fieldNameIcons.rankFeaturesFieldAriaLabel": "Fonctionnalités de rang",
"discover.fieldNameIcons.recordAriaLabel": "Enregistrements",
"discover.fieldNameIcons.shapeFieldAriaLabel": "Forme",
"discover.fieldNameIcons.sourceFieldAriaLabel": "Champ source",
"discover.fieldNameIcons.stringFieldAriaLabel": "Chaîne",
"discover.fieldNameIcons.textFieldAriaLabel": "Texte",
"discover.fieldNameIcons.unknownFieldAriaLabel": "Champ inconnu",
"discover.fieldNameIcons.versionFieldAriaLabel": "Version",
"fieldUtils.fieldNameIcons.binaryAriaLabel": "Binaire",
"fieldUtils.fieldNameIcons.booleanAriaLabel": "Booléen",
"fieldUtils.fieldNameIcons.conflictFieldAriaLabel": "Conflit",
"fieldUtils.fieldNameIcons.counterFieldAriaLabel": "Indicateur de compteur",
"fieldUtils.fieldNameIcons.dateFieldAriaLabel": "Date",
"fieldUtils.fieldNameIcons.dateRangeFieldAriaLabel": "Plage de dates",
"fieldUtils.fieldNameIcons.denseVectorFieldAriaLabel": "Vecteur dense",
"fieldUtils.fieldNameIcons.flattenedFieldAriaLabel": "Lissé",
"fieldUtils.fieldNameIcons.gaugeFieldAriaLabel": "Indicateur de jauge",
"fieldUtils.fieldNameIcons.geoPointFieldAriaLabel": "Point géographique",
"fieldUtils.fieldNameIcons.geoShapeFieldAriaLabel": "Forme géométrique",
"fieldUtils.fieldNameIcons.histogramFieldAriaLabel": "Histogramme",
"fieldUtils.fieldNameIcons.ipAddressFieldAriaLabel": "Adresse IP",
"fieldUtils.fieldNameIcons.ipRangeFieldAriaLabel": "Plage d'IP",
"fieldUtils.fieldNameIcons.keywordFieldAriaLabel": "Mot-clé",
"fieldUtils.fieldNameIcons.murmur3FieldAriaLabel": "Murmur3",
"fieldUtils.fieldNameIcons.nestedFieldAriaLabel": "Imbriqué",
"fieldUtils.fieldNameIcons.numberFieldAriaLabel": "Nombre",
"fieldUtils.fieldNameIcons.pointFieldAriaLabel": "Point",
"fieldUtils.fieldNameIcons.rankFeatureFieldAriaLabel": "Fonctionnalité de rang",
"fieldUtils.fieldNameIcons.rankFeaturesFieldAriaLabel": "Fonctionnalités de rang",
"fieldUtils.fieldNameIcons.recordAriaLabel": "Enregistrements",
"fieldUtils.fieldNameIcons.shapeFieldAriaLabel": "Forme",
"fieldUtils.fieldNameIcons.sourceFieldAriaLabel": "Champ source",
"fieldUtils.fieldNameIcons.stringFieldAriaLabel": "Chaîne",
"fieldUtils.fieldNameIcons.textFieldAriaLabel": "Texte",
"fieldUtils.fieldNameIcons.unknownFieldAriaLabel": "Champ inconnu",
"fieldUtils.fieldNameIcons.versionFieldAriaLabel": "Version",
"unifiedDocViewer.docView.table.actions.label": "Actions",
"unifiedDocViewer.docView.table.actions.open": "Actions ouvertes",
"unifiedDocViewer.docView.table.ignored.multiAboveTooltip": "Une ou plusieurs valeurs dans ce champ sont trop longues et ne peuvent pas être recherchées ni filtrées.",
@ -5645,33 +5645,33 @@
"unifiedFieldList.fieldList.noFieldsCallout.noFields.tryText": "Essayer :",
"unifiedFieldList.fieldList.noFieldsCallout.noFieldsLabel": "Aucun champ n'existe dans cette vue de données.",
"unifiedFieldList.fieldList.noFieldsCallout.noFilteredFieldsLabel": "Aucun champ ne correspond aux filtres sélectionnés.",
"unifiedFieldList.fieldNameDescription.binaryField": "Valeur binaire encodée en tant que chaîne Base64.",
"unifiedFieldList.fieldNameDescription.booleanField": "Valeurs vraies ou fausses.",
"unifiedFieldList.fieldNameDescription.conflictField": "Le champ possède des valeurs de différents types. Corrigez le problème dans Gestion > Vues de données.",
"unifiedFieldList.fieldNameDescription.counterField": "Nombre qui ne peut qu'augmenter ou être réinitialisé sur 0 (zéro). Disponible uniquement pour les champs numériques et aggregate_metric_double.",
"unifiedFieldList.fieldNameDescription.dateField": "Chaîne de date ou nombre de secondes ou de millisecondes depuis 1/1/1970.",
"unifiedFieldList.fieldNameDescription.dateRangeField": "Plage de valeurs de date.",
"unifiedFieldList.fieldNameDescription.denseVectorField": "Enregistre les vecteurs denses des valeurs Éléments flottants.",
"unifiedFieldList.fieldNameDescription.flattenedField": "Objet JSON tout entier en tant que valeur de champ unique.",
"unifiedFieldList.fieldNameDescription.gaugeField": "Nombre qui peut augmenter ou diminuer. Disponible uniquement pour les champs numériques et aggregate_metric_double.",
"unifiedFieldList.fieldNameDescription.geoPointField": "Points de latitude et de longitude.",
"unifiedFieldList.fieldNameDescription.geoShapeField": "Formes complexes, telles que des polygones.",
"unifiedFieldList.fieldNameDescription.histogramField": "Valeurs numériques pré-agrégées sous forme d'histogramme.",
"unifiedFieldList.fieldNameDescription.ipAddressField": "Adresses IPv4 et IPv6.",
"unifiedFieldList.fieldNameDescription.ipAddressRangeField": "Plage de valeurs IP prenant en charge les adresses IPv4 ou IPv6 (ou les 2).",
"unifiedFieldList.fieldNameDescription.keywordField": "Contenu structuré tel qu'un ID, une adresse e-mail, un nom d'hôte, un code de statut, ou une balise.",
"unifiedFieldList.fieldNameDescription.murmur3Field": "Champ qui calcule et stocke les hachages de valeurs.",
"unifiedFieldList.fieldNameDescription.nestedField": "Objet JSON qui conserve la relation entre ses sous-champs.",
"unifiedFieldList.fieldNameDescription.numberField": "Valeurs Long, Entier, Court, Octet, Double et Élément flottant.",
"unifiedFieldList.fieldNameDescription.pointField": "Points cartésiens arbitraires.",
"unifiedFieldList.fieldNameDescription.rankFeatureField": "Enregistre une fonctionnalité numérique pour augmenter le nombre de résultats au moment de la requête.",
"unifiedFieldList.fieldNameDescription.rankFeaturesField": "Enregistre des fonctionnalités numériques pour augmenter le nombre de résultats au moment de la requête.",
"unifiedFieldList.fieldNameDescription.recordField": "Nombre d'enregistrements.",
"unifiedFieldList.fieldNameDescription.shapeField": "Géométries cartésiennes arbitraires.",
"unifiedFieldList.fieldNameDescription.stringField": "Texte intégral tel que le corps d'un e-mail ou la description d'un produit.",
"unifiedFieldList.fieldNameDescription.textField": "Texte intégral tel que le corps d'un e-mail ou la description d'un produit.",
"unifiedFieldList.fieldNameDescription.unknownField": "Champ inconnu",
"unifiedFieldList.fieldNameDescription.versionField": "Versions des logiciels. Prend en charge les règles de priorité de la Gestion sémantique des versions.",
"fieldUtils.fieldNameDescription.binaryField": "Valeur binaire encodée en tant que chaîne Base64.",
"fieldUtils.fieldNameDescription.booleanField": "Valeurs vraies ou fausses.",
"fieldUtils.fieldNameDescription.conflictField": "Le champ possède des valeurs de différents types. Corrigez le problème dans Gestion > Vues de données.",
"fieldUtils.fieldNameDescription.counterField": "Nombre qui ne peut qu'augmenter ou être réinitialisé sur 0 (zéro). Disponible uniquement pour les champs numériques et aggregate_metric_double.",
"fieldUtils.fieldNameDescription.dateField": "Chaîne de date ou nombre de secondes ou de millisecondes depuis 1/1/1970.",
"fieldUtils.fieldNameDescription.dateRangeField": "Plage de valeurs de date.",
"fieldUtils.fieldNameDescription.denseVectorField": "Enregistre les vecteurs denses des valeurs Éléments flottants.",
"fieldUtils.fieldNameDescription.flattenedField": "Objet JSON tout entier en tant que valeur de champ unique.",
"fieldUtils.fieldNameDescription.gaugeField": "Nombre qui peut augmenter ou diminuer. Disponible uniquement pour les champs numériques et aggregate_metric_double.",
"fieldUtils.fieldNameDescription.geoPointField": "Points de latitude et de longitude.",
"fieldUtils.fieldNameDescription.geoShapeField": "Formes complexes, telles que des polygones.",
"fieldUtils.fieldNameDescription.histogramField": "Valeurs numériques pré-agrégées sous forme d'histogramme.",
"fieldUtils.fieldNameDescription.ipAddressField": "Adresses IPv4 et IPv6.",
"fieldUtils.fieldNameDescription.ipAddressRangeField": "Plage de valeurs IP prenant en charge les adresses IPv4 ou IPv6 (ou les 2).",
"fieldUtils.fieldNameDescription.keywordField": "Contenu structuré tel qu'un ID, une adresse e-mail, un nom d'hôte, un code de statut, ou une balise.",
"fieldUtils.fieldNameDescription.murmur3Field": "Champ qui calcule et stocke les hachages de valeurs.",
"fieldUtils.fieldNameDescription.nestedField": "Objet JSON qui conserve la relation entre ses sous-champs.",
"fieldUtils.fieldNameDescription.numberField": "Valeurs Long, Entier, Court, Octet, Double et Élément flottant.",
"fieldUtils.fieldNameDescription.pointField": "Points cartésiens arbitraires.",
"fieldUtils.fieldNameDescription.rankFeatureField": "Enregistre une fonctionnalité numérique pour augmenter le nombre de résultats au moment de la requête.",
"fieldUtils.fieldNameDescription.rankFeaturesField": "Enregistre des fonctionnalités numériques pour augmenter le nombre de résultats au moment de la requête.",
"fieldUtils.fieldNameDescription.recordField": "Nombre d'enregistrements.",
"fieldUtils.fieldNameDescription.shapeField": "Géométries cartésiennes arbitraires.",
"fieldUtils.fieldNameDescription.stringField": "Texte intégral tel que le corps d'un e-mail ou la description d'un produit.",
"fieldUtils.fieldNameDescription.textField": "Texte intégral tel que le corps d'un e-mail ou la description d'un produit.",
"fieldUtils.fieldNameDescription.unknownField": "Champ inconnu",
"fieldUtils.fieldNameDescription.versionField": "Versions des logiciels. Prend en charge les règles de priorité de la Gestion sémantique des versions.",
"unifiedFieldList.fieldNameSearch.filterByNameLabel": "Rechercher les noms de champs",
"unifiedFieldList.fieldPopover.addExistsFilterLabel": "Filtrer sur le champ",
"unifiedFieldList.fieldPopover.deleteFieldLabel": "Supprimer le champ de la vue de données",

View file

@ -2413,34 +2413,34 @@
"discover.viewAlert.searchSourceErrorTitle": "検索ソースの取得エラー",
"discover.viewModes.document.label": "ドキュメント",
"discover.viewModes.fieldStatistics.label": "フィールド統計情報",
"discover.fieldNameIcons.binaryAriaLabel": "バイナリー",
"discover.fieldNameIcons.booleanAriaLabel": "ブール",
"discover.fieldNameIcons.conflictFieldAriaLabel": "競合",
"discover.fieldNameIcons.counterFieldAriaLabel": "カウンターメトリック",
"discover.fieldNameIcons.dateFieldAriaLabel": "日付",
"discover.fieldNameIcons.dateRangeFieldAriaLabel": "日付範囲",
"discover.fieldNameIcons.denseVectorFieldAriaLabel": "密集ベクトル",
"discover.fieldNameIcons.flattenedFieldAriaLabel": "平坦化済み",
"discover.fieldNameIcons.gaugeFieldAriaLabel": "ゲージメトリック",
"discover.fieldNameIcons.geoPointFieldAriaLabel": "地理ポイント",
"discover.fieldNameIcons.geoShapeFieldAriaLabel": "地理情報図形",
"discover.fieldNameIcons.histogramFieldAriaLabel": "ヒストグラム",
"discover.fieldNameIcons.ipAddressFieldAriaLabel": "IP アドレス",
"discover.fieldNameIcons.ipRangeFieldAriaLabel": "IP範囲",
"discover.fieldNameIcons.keywordFieldAriaLabel": "キーワード",
"discover.fieldNameIcons.murmur3FieldAriaLabel": "Murmur3",
"discover.fieldNameIcons.nestedFieldAriaLabel": "ネスト済み",
"discover.fieldNameIcons.numberFieldAriaLabel": "数字",
"discover.fieldNameIcons.pointFieldAriaLabel": "点",
"discover.fieldNameIcons.rankFeatureFieldAriaLabel": "ランク特性",
"discover.fieldNameIcons.rankFeaturesFieldAriaLabel": "ランク特性",
"discover.fieldNameIcons.recordAriaLabel": "記録",
"discover.fieldNameIcons.shapeFieldAriaLabel": "形状",
"discover.fieldNameIcons.sourceFieldAriaLabel": "ソースフィールド",
"discover.fieldNameIcons.stringFieldAriaLabel": "文字列",
"discover.fieldNameIcons.textFieldAriaLabel": "テキスト",
"discover.fieldNameIcons.unknownFieldAriaLabel": "不明なフィールド",
"discover.fieldNameIcons.versionFieldAriaLabel": "バージョン",
"fieldUtils.fieldNameIcons.binaryAriaLabel": "バイナリー",
"fieldUtils.fieldNameIcons.booleanAriaLabel": "ブール",
"fieldUtils.fieldNameIcons.conflictFieldAriaLabel": "競合",
"fieldUtils.fieldNameIcons.counterFieldAriaLabel": "カウンターメトリック",
"fieldUtils.fieldNameIcons.dateFieldAriaLabel": "日付",
"fieldUtils.fieldNameIcons.dateRangeFieldAriaLabel": "日付範囲",
"fieldUtils.fieldNameIcons.denseVectorFieldAriaLabel": "密集ベクトル",
"fieldUtils.fieldNameIcons.flattenedFieldAriaLabel": "平坦化済み",
"fieldUtils.fieldNameIcons.gaugeFieldAriaLabel": "ゲージメトリック",
"fieldUtils.fieldNameIcons.geoPointFieldAriaLabel": "地理ポイント",
"fieldUtils.fieldNameIcons.geoShapeFieldAriaLabel": "地理情報図形",
"fieldUtils.fieldNameIcons.histogramFieldAriaLabel": "ヒストグラム",
"fieldUtils.fieldNameIcons.ipAddressFieldAriaLabel": "IP アドレス",
"fieldUtils.fieldNameIcons.ipRangeFieldAriaLabel": "IP範囲",
"fieldUtils.fieldNameIcons.keywordFieldAriaLabel": "キーワード",
"fieldUtils.fieldNameIcons.murmur3FieldAriaLabel": "Murmur3",
"fieldUtils.fieldNameIcons.nestedFieldAriaLabel": "ネスト済み",
"fieldUtils.fieldNameIcons.numberFieldAriaLabel": "数字",
"fieldUtils.fieldNameIcons.pointFieldAriaLabel": "点",
"fieldUtils.fieldNameIcons.rankFeatureFieldAriaLabel": "ランク特性",
"fieldUtils.fieldNameIcons.rankFeaturesFieldAriaLabel": "ランク特性",
"fieldUtils.fieldNameIcons.recordAriaLabel": "記録",
"fieldUtils.fieldNameIcons.shapeFieldAriaLabel": "形状",
"fieldUtils.fieldNameIcons.sourceFieldAriaLabel": "ソースフィールド",
"fieldUtils.fieldNameIcons.stringFieldAriaLabel": "文字列",
"fieldUtils.fieldNameIcons.textFieldAriaLabel": "テキスト",
"fieldUtils.fieldNameIcons.unknownFieldAriaLabel": "不明なフィールド",
"fieldUtils.fieldNameIcons.versionFieldAriaLabel": "バージョン",
"unifiedDocViewer.docView.table.actions.label": "アクション",
"unifiedDocViewer.docView.table.actions.open": "アクションを開く",
"unifiedDocViewer.docView.table.ignored.multiAboveTooltip": "このフィールドの1つ以上の値が長すぎるため、検索またはフィルタリングできません。",
@ -5661,33 +5661,33 @@
"unifiedFieldList.fieldList.noFieldsCallout.noFields.tryText": "試行対象:",
"unifiedFieldList.fieldList.noFieldsCallout.noFieldsLabel": "このデータビューにはフィールドがありません。",
"unifiedFieldList.fieldList.noFieldsCallout.noFilteredFieldsLabel": "選択したフィルターと一致するフィールドはありません。",
"unifiedFieldList.fieldNameDescription.binaryField": "Base64文字列としてエンコードされたバイナリ値",
"unifiedFieldList.fieldNameDescription.booleanField": "True および False 値。",
"unifiedFieldList.fieldNameDescription.conflictField": "フィールドには異なる型の値があります。[管理 > データビュー]で解決してください。",
"unifiedFieldList.fieldNameDescription.counterField": "0ゼロに増加またはリセットのみされる数値。数値およびaggregate_metric_doubleフィールドでのみ使用可能です。",
"unifiedFieldList.fieldNameDescription.dateField": "日付文字列、または1/1/1970以降の秒またはミリ秒の数値。",
"unifiedFieldList.fieldNameDescription.dateRangeField": "日付値の範囲。",
"unifiedFieldList.fieldNameDescription.denseVectorField": "浮動小数点数値の密ベクトルを記録します。",
"unifiedFieldList.fieldNameDescription.flattenedField": "1つのフィールド値としてのJSONオブジェクト全体。",
"unifiedFieldList.fieldNameDescription.gaugeField": "増減可能な数値。数値およびaggregate_metric_doubleフィールドでのみ使用可能です。",
"unifiedFieldList.fieldNameDescription.geoPointField": "緯度および経度点。",
"unifiedFieldList.fieldNameDescription.geoShapeField": "多角形などの複雑な図形。",
"unifiedFieldList.fieldNameDescription.histogramField": "ヒストグラムの形式の集計された数値。",
"unifiedFieldList.fieldNameDescription.ipAddressField": "IPv4およびIPv6アドレス。",
"unifiedFieldList.fieldNameDescription.ipAddressRangeField": "IPv4またはIPv6または混合のアドレスをサポートするIP値の範囲。",
"unifiedFieldList.fieldNameDescription.keywordField": "ID、電子メールアドレス、ホスト名、ステータスコード、タグなどの構造化されたコンテンツ。",
"unifiedFieldList.fieldNameDescription.murmur3Field": "値のハッシュタグを計算して格納するフィールド。",
"unifiedFieldList.fieldNameDescription.nestedField": "サブフィールド間の関係を保持するJSONオブジェクト。",
"unifiedFieldList.fieldNameDescription.numberField": "長整数、整数、短整数、バイト、倍精度浮動小数点数、浮動小数点数の値。",
"unifiedFieldList.fieldNameDescription.pointField": "任意の直交点。",
"unifiedFieldList.fieldNameDescription.rankFeatureField": "クエリ時のヒット数を増やすために、数値機能を記録します。",
"unifiedFieldList.fieldNameDescription.rankFeaturesField": "クエリ時のヒット数を増やすために、数値機能を記録します。",
"unifiedFieldList.fieldNameDescription.recordField": "レコード数。",
"unifiedFieldList.fieldNameDescription.shapeField": "任意の解析幾何。",
"unifiedFieldList.fieldNameDescription.stringField": "電子メール本文や製品説明などの全文テキスト。",
"unifiedFieldList.fieldNameDescription.textField": "電子メール本文や製品説明などの全文テキスト。",
"unifiedFieldList.fieldNameDescription.unknownField": "不明なフィールド",
"unifiedFieldList.fieldNameDescription.versionField": "ソフトウェアバージョン。「セマンティックバージョニング」優先度ルールをサポートします。",
"fieldUtils.fieldNameDescription.binaryField": "Base64文字列としてエンコードされたバイナリ値",
"fieldUtils.fieldNameDescription.booleanField": "True および False 値。",
"fieldUtils.fieldNameDescription.conflictField": "フィールドには異なる型の値があります。[管理 > データビュー]で解決してください。",
"fieldUtils.fieldNameDescription.counterField": "0ゼロに増加またはリセットのみされる数値。数値およびaggregate_metric_doubleフィールドでのみ使用可能です。",
"fieldUtils.fieldNameDescription.dateField": "日付文字列、または1/1/1970以降の秒またはミリ秒の数値。",
"fieldUtils.fieldNameDescription.dateRangeField": "日付値の範囲。",
"fieldUtils.fieldNameDescription.denseVectorField": "浮動小数点数値の密ベクトルを記録します。",
"fieldUtils.fieldNameDescription.flattenedField": "1つのフィールド値としてのJSONオブジェクト全体。",
"fieldUtils.fieldNameDescription.gaugeField": "増減可能な数値。数値およびaggregate_metric_doubleフィールドでのみ使用可能です。",
"fieldUtils.fieldNameDescription.geoPointField": "緯度および経度点。",
"fieldUtils.fieldNameDescription.geoShapeField": "多角形などの複雑な図形。",
"fieldUtils.fieldNameDescription.histogramField": "ヒストグラムの形式の集計された数値。",
"fieldUtils.fieldNameDescription.ipAddressField": "IPv4およびIPv6アドレス。",
"fieldUtils.fieldNameDescription.ipAddressRangeField": "IPv4またはIPv6または混合のアドレスをサポートするIP値の範囲。",
"fieldUtils.fieldNameDescription.keywordField": "ID、電子メールアドレス、ホスト名、ステータスコード、タグなどの構造化されたコンテンツ。",
"fieldUtils.fieldNameDescription.murmur3Field": "値のハッシュタグを計算して格納するフィールド。",
"fieldUtils.fieldNameDescription.nestedField": "サブフィールド間の関係を保持するJSONオブジェクト。",
"fieldUtils.fieldNameDescription.numberField": "長整数、整数、短整数、バイト、倍精度浮動小数点数、浮動小数点数の値。",
"fieldUtils.fieldNameDescription.pointField": "任意の直交点。",
"fieldUtils.fieldNameDescription.rankFeatureField": "クエリ時のヒット数を増やすために、数値機能を記録します。",
"fieldUtils.fieldNameDescription.rankFeaturesField": "クエリ時のヒット数を増やすために、数値機能を記録します。",
"fieldUtils.fieldNameDescription.recordField": "レコード数。",
"fieldUtils.fieldNameDescription.shapeField": "任意の解析幾何。",
"fieldUtils.fieldNameDescription.stringField": "電子メール本文や製品説明などの全文テキスト。",
"fieldUtils.fieldNameDescription.textField": "電子メール本文や製品説明などの全文テキスト。",
"fieldUtils.fieldNameDescription.unknownField": "不明なフィールド",
"fieldUtils.fieldNameDescription.versionField": "ソフトウェアバージョン。「セマンティックバージョニング」優先度ルールをサポートします。",
"unifiedFieldList.fieldNameSearch.filterByNameLabel": "検索フィールド名",
"unifiedFieldList.fieldPopover.addExistsFilterLabel": "フィールド表示のフィルター",
"unifiedFieldList.fieldPopover.deleteFieldLabel": "データビューフィールドを削除",

View file

@ -2413,34 +2413,34 @@
"discover.viewAlert.searchSourceErrorTitle": "提取搜索源时出错",
"discover.viewModes.document.label": "文档",
"discover.viewModes.fieldStatistics.label": "字段统计信息",
"discover.fieldNameIcons.binaryAriaLabel": "二进制",
"discover.fieldNameIcons.booleanAriaLabel": "布尔型",
"discover.fieldNameIcons.conflictFieldAriaLabel": "冲突",
"discover.fieldNameIcons.counterFieldAriaLabel": "计数器指标",
"discover.fieldNameIcons.dateFieldAriaLabel": "日期",
"discover.fieldNameIcons.dateRangeFieldAriaLabel": "日期范围",
"discover.fieldNameIcons.denseVectorFieldAriaLabel": "密集向量",
"discover.fieldNameIcons.flattenedFieldAriaLabel": "扁平",
"discover.fieldNameIcons.gaugeFieldAriaLabel": "仪表盘指标",
"discover.fieldNameIcons.geoPointFieldAriaLabel": "地理点",
"discover.fieldNameIcons.geoShapeFieldAriaLabel": "几何形状",
"discover.fieldNameIcons.histogramFieldAriaLabel": "直方图",
"discover.fieldNameIcons.ipAddressFieldAriaLabel": "IP 地址",
"discover.fieldNameIcons.ipRangeFieldAriaLabel": "IP 范围",
"discover.fieldNameIcons.keywordFieldAriaLabel": "关键字",
"discover.fieldNameIcons.murmur3FieldAriaLabel": "Murmur3",
"discover.fieldNameIcons.nestedFieldAriaLabel": "嵌套",
"discover.fieldNameIcons.numberFieldAriaLabel": "数字",
"discover.fieldNameIcons.pointFieldAriaLabel": "点",
"discover.fieldNameIcons.rankFeatureFieldAriaLabel": "排名特征",
"discover.fieldNameIcons.rankFeaturesFieldAriaLabel": "排名特征",
"discover.fieldNameIcons.recordAriaLabel": "记录",
"discover.fieldNameIcons.shapeFieldAriaLabel": "形状",
"discover.fieldNameIcons.sourceFieldAriaLabel": "源字段",
"discover.fieldNameIcons.stringFieldAriaLabel": "字符串",
"discover.fieldNameIcons.textFieldAriaLabel": "文本",
"discover.fieldNameIcons.unknownFieldAriaLabel": "未知字段",
"discover.fieldNameIcons.versionFieldAriaLabel": "版本",
"fieldUtils.fieldNameIcons.binaryAriaLabel": "二进制",
"fieldUtils.fieldNameIcons.booleanAriaLabel": "布尔型",
"fieldUtils.fieldNameIcons.conflictFieldAriaLabel": "冲突",
"fieldUtils.fieldNameIcons.counterFieldAriaLabel": "计数器指标",
"fieldUtils.fieldNameIcons.dateFieldAriaLabel": "日期",
"fieldUtils.fieldNameIcons.dateRangeFieldAriaLabel": "日期范围",
"fieldUtils.fieldNameIcons.denseVectorFieldAriaLabel": "密集向量",
"fieldUtils.fieldNameIcons.flattenedFieldAriaLabel": "扁平",
"fieldUtils.fieldNameIcons.gaugeFieldAriaLabel": "仪表盘指标",
"fieldUtils.fieldNameIcons.geoPointFieldAriaLabel": "地理点",
"fieldUtils.fieldNameIcons.geoShapeFieldAriaLabel": "几何形状",
"fieldUtils.fieldNameIcons.histogramFieldAriaLabel": "直方图",
"fieldUtils.fieldNameIcons.ipAddressFieldAriaLabel": "IP 地址",
"fieldUtils.fieldNameIcons.ipRangeFieldAriaLabel": "IP 范围",
"fieldUtils.fieldNameIcons.keywordFieldAriaLabel": "关键字",
"fieldUtils.fieldNameIcons.murmur3FieldAriaLabel": "Murmur3",
"fieldUtils.fieldNameIcons.nestedFieldAriaLabel": "嵌套",
"fieldUtils.fieldNameIcons.numberFieldAriaLabel": "数字",
"fieldUtils.fieldNameIcons.pointFieldAriaLabel": "点",
"fieldUtils.fieldNameIcons.rankFeatureFieldAriaLabel": "排名特征",
"fieldUtils.fieldNameIcons.rankFeaturesFieldAriaLabel": "排名特征",
"fieldUtils.fieldNameIcons.recordAriaLabel": "记录",
"fieldUtils.fieldNameIcons.shapeFieldAriaLabel": "形状",
"fieldUtils.fieldNameIcons.sourceFieldAriaLabel": "源字段",
"fieldUtils.fieldNameIcons.stringFieldAriaLabel": "字符串",
"fieldUtils.fieldNameIcons.textFieldAriaLabel": "文本",
"fieldUtils.fieldNameIcons.unknownFieldAriaLabel": "未知字段",
"fieldUtils.fieldNameIcons.versionFieldAriaLabel": "版本",
"unifiedDocViewer.docView.table.actions.label": "操作",
"unifiedDocViewer.docView.table.actions.open": "打开操作",
"unifiedDocViewer.docView.table.ignored.multiAboveTooltip": "此字段中的一个或多个值过长,无法搜索或筛选。",
@ -5660,33 +5660,33 @@
"unifiedFieldList.fieldList.noFieldsCallout.noFields.tryText": "尝试:",
"unifiedFieldList.fieldList.noFieldsCallout.noFieldsLabel": "在此数据视图中不存在任何字段。",
"unifiedFieldList.fieldList.noFieldsCallout.noFilteredFieldsLabel": "没有字段匹配选定筛选。",
"unifiedFieldList.fieldNameDescription.binaryField": "编码为 Base64 字符串的二进制值。",
"unifiedFieldList.fieldNameDescription.booleanField": "True 和 False 值。",
"unifiedFieldList.fieldNameDescription.conflictField": "字体具有不同类型的值。在“管理”>“数据视图”中解析。",
"unifiedFieldList.fieldNameDescription.counterField": "只会增大或重置为 0的数字。仅适用于数字和 aggregate_metric_double 字段。",
"unifiedFieldList.fieldNameDescription.dateField": "日期字符串或 1/1/1970 以来的秒数或毫秒数。",
"unifiedFieldList.fieldNameDescription.dateRangeField": "日期值的范围。",
"unifiedFieldList.fieldNameDescription.denseVectorField": "记录浮点值的密集向量。",
"unifiedFieldList.fieldNameDescription.flattenedField": "整个 JSON 对象作为单一字段值。",
"unifiedFieldList.fieldNameDescription.gaugeField": "可以增大或减小的数字。仅适用于数字和 aggregate_metric_double 字段。",
"unifiedFieldList.fieldNameDescription.geoPointField": "纬度和经度点。",
"unifiedFieldList.fieldNameDescription.geoShapeField": "复杂形状,如多边形。",
"unifiedFieldList.fieldNameDescription.histogramField": "直方图形式的预聚合数字值。",
"unifiedFieldList.fieldNameDescription.ipAddressField": "IPv4 和 IPv6 地址。",
"unifiedFieldList.fieldNameDescription.ipAddressRangeField": "支持 IPv4 或 IPv6或混合地址的 IP 值的范围。",
"unifiedFieldList.fieldNameDescription.keywordField": "结构化内容,如 ID、电子邮件地址、主机名、状态代码或标签。",
"unifiedFieldList.fieldNameDescription.murmur3Field": "计算和存储值哈希的字段。",
"unifiedFieldList.fieldNameDescription.nestedField": "保留其子字段之间关系的 JSON 对象。",
"unifiedFieldList.fieldNameDescription.numberField": "长整型、整数、短整型、字节、双精度和浮点值。",
"unifiedFieldList.fieldNameDescription.pointField": "任意笛卡尔点。",
"unifiedFieldList.fieldNameDescription.rankFeatureField": "记录数字特征以提高查询时的命中数。",
"unifiedFieldList.fieldNameDescription.rankFeaturesField": "记录数字特征以提高查询时的命中数。",
"unifiedFieldList.fieldNameDescription.recordField": "记录计数。",
"unifiedFieldList.fieldNameDescription.shapeField": "任意笛卡尔几何形状。",
"unifiedFieldList.fieldNameDescription.stringField": "全文本,如电子邮件正文或产品描述。",
"unifiedFieldList.fieldNameDescription.textField": "全文本,如电子邮件正文或产品描述。",
"unifiedFieldList.fieldNameDescription.unknownField": "未知字段",
"unifiedFieldList.fieldNameDescription.versionField": "软件版本。支持“语义版本控制”优先规则。",
"fieldUtils.fieldNameDescription.binaryField": "编码为 Base64 字符串的二进制值。",
"fieldUtils.fieldNameDescription.booleanField": "True 和 False 值。",
"fieldUtils.fieldNameDescription.conflictField": "字体具有不同类型的值。在“管理”>“数据视图”中解析。",
"fieldUtils.fieldNameDescription.counterField": "只会增大或重置为 0的数字。仅适用于数字和 aggregate_metric_double 字段。",
"fieldUtils.fieldNameDescription.dateField": "日期字符串或 1/1/1970 以来的秒数或毫秒数。",
"fieldUtils.fieldNameDescription.dateRangeField": "日期值的范围。",
"fieldUtils.fieldNameDescription.denseVectorField": "记录浮点值的密集向量。",
"fieldUtils.fieldNameDescription.flattenedField": "整个 JSON 对象作为单一字段值。",
"fieldUtils.fieldNameDescription.gaugeField": "可以增大或减小的数字。仅适用于数字和 aggregate_metric_double 字段。",
"fieldUtils.fieldNameDescription.geoPointField": "纬度和经度点。",
"fieldUtils.fieldNameDescription.geoShapeField": "复杂形状,如多边形。",
"fieldUtils.fieldNameDescription.histogramField": "直方图形式的预聚合数字值。",
"fieldUtils.fieldNameDescription.ipAddressField": "IPv4 和 IPv6 地址。",
"fieldUtils.fieldNameDescription.ipAddressRangeField": "支持 IPv4 或 IPv6或混合地址的 IP 值的范围。",
"fieldUtils.fieldNameDescription.keywordField": "结构化内容,如 ID、电子邮件地址、主机名、状态代码或标签。",
"fieldUtils.fieldNameDescription.murmur3Field": "计算和存储值哈希的字段。",
"fieldUtils.fieldNameDescription.nestedField": "保留其子字段之间关系的 JSON 对象。",
"fieldUtils.fieldNameDescription.numberField": "长整型、整数、短整型、字节、双精度和浮点值。",
"fieldUtils.fieldNameDescription.pointField": "任意笛卡尔点。",
"fieldUtils.fieldNameDescription.rankFeatureField": "记录数字特征以提高查询时的命中数。",
"fieldUtils.fieldNameDescription.rankFeaturesField": "记录数字特征以提高查询时的命中数。",
"fieldUtils.fieldNameDescription.recordField": "记录计数。",
"fieldUtils.fieldNameDescription.shapeField": "任意笛卡尔几何形状。",
"fieldUtils.fieldNameDescription.stringField": "全文本,如电子邮件正文或产品描述。",
"fieldUtils.fieldNameDescription.textField": "全文本,如电子邮件正文或产品描述。",
"fieldUtils.fieldNameDescription.unknownField": "未知字段",
"fieldUtils.fieldNameDescription.versionField": "软件版本。支持“语义版本控制”优先规则。",
"unifiedFieldList.fieldNameSearch.filterByNameLabel": "搜索字段名称",
"unifiedFieldList.fieldPopover.addExistsFilterLabel": "筛留存在的字段",
"unifiedFieldList.fieldPopover.deleteFieldLabel": "删除数据视图字段",

View file

@ -489,7 +489,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const documentCell = await dataGrid.getCellElement(0, 3);
const firstRowContent = await documentCell.getVisibleText();
expect(firstRowContent.includes('runtime-message-fieldmock-message_id')).to.be.equal(true);
expect(firstRowContent.includes('runtime-message-fieldmock-message')).to.be.equal(true);
expect(await dataGrid.getDocCount()).to.be(5);
});
@ -503,7 +503,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const documentCell = await dataGrid.getCellElement(0, 3);
const firstRowContent = await documentCell.getVisibleText();
expect(firstRowContent.includes('runtime-message-fieldmock-message_id')).to.be.equal(true);
expect(firstRowContent.includes('runtime-message-fieldmock-message')).to.be.equal(true);
});
it('should display results after data view removal on clicking prev generated link', async () => {

View file

@ -4512,6 +4512,10 @@
version "0.0.0"
uid ""
"@kbn/field-utils@link:packages/kbn-field-utils":
version "0.0.0"
uid ""
"@kbn/file-upload-plugin@link:x-pack/plugins/file_upload":
version "0.0.0"
uid ""