diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/__snapshots__/indexed_fields_table.test.tsx.snap b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/__snapshots__/indexed_fields_table.test.tsx.snap index 8b6e0a1682750..fb463d0a5fb18 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/__snapshots__/indexed_fields_table.test.tsx.snap +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/__snapshots__/indexed_fields_table.test.tsx.snap @@ -116,7 +116,7 @@ exports[`IndexedFieldsTable IndexedFieldsTable with rollup index pattern should "isUserEditable": false, "kbnType": undefined, "name": "conflictingField", - "type": "keyword, long", + "type": "conflict", }, Object { "displayName": "amount", @@ -274,7 +274,7 @@ exports[`IndexedFieldsTable should render normally 1`] = ` "isUserEditable": false, "kbnType": undefined, "name": "conflictingField", - "type": "keyword, long", + "type": "conflict", }, Object { "displayName": "amount", diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/__snapshots__/table.test.tsx.snap b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/__snapshots__/table.test.tsx.snap index f876b15bc86c0..d32de9c7abb56 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/__snapshots__/table.test.tsx.snap +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/__snapshots__/table.test.tsx.snap @@ -1,5 +1,86 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Table render conflict summary modal 1`] = ` + + + +

+ +

+
+
+ + +

+ + message + , + } + } + /> +

+ + } + responsive={true} + rowHeader="firstName" + tableCaption="Demo of EuiBasicTable" + tableLayout="auto" + /> +
+
+ + + + + +
+`; + exports[`Table render name 1`] = ` customer @@ -26,15 +107,17 @@ exports[`Table render name 2`] = ` exports[`Table should render conflicting type 1`] = ` - conflict -   - + iconOnClick={[Function]} + iconOnClickAriaLabel="Conflict Detail" + iconType="alert" + onClick={[Function]} + onClickAriaLabel="Conflict Detail" + > + Conflict + `; @@ -160,6 +243,14 @@ exports[`Table should render normally 1`] = ` "type": "date", }, Object { + "conflictDescriptions": Object { + "keyword": Array [ + "index_a", + ], + "long": Array [ + "index_b", + ], + }, "displayName": "conflictingField", "excluded": false, "hasRuntime": false, diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx index 71b2e59aefc81..ec18665ccbaf3 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx @@ -10,7 +10,8 @@ import React from 'react'; import { shallow } from 'enzyme'; import { IndexPattern } from 'src/plugins/data/public'; import { IndexedFieldItem } from '../../types'; -import { Table, renderFieldName } from './table'; +import { Table, renderFieldName, getConflictModalContent } from './table'; +import { overlayServiceMock } from 'src/core/public/mocks'; const indexPattern = { timeFieldName: 'timestamp', @@ -43,6 +44,7 @@ const items: IndexedFieldItem[] = [ { name: 'conflictingField', displayName: 'conflictingField', + conflictDescriptions: { keyword: ['index_a'], long: ['index_b'] }, type: 'text, long', kbnType: 'conflict', info: [], @@ -81,7 +83,13 @@ const renderTable = ( } ) => shallow( - {}} /> +
{}} + openModal={overlayServiceMock.createStartContract().openModal} + /> ); describe('Table', () => { @@ -116,7 +124,12 @@ describe('Table', () => { test('should render conflicting type', () => { const tableCell = shallow( - renderTable().prop('columns')[1].render('conflict', { kbnType: 'conflict' }) + renderTable() + .prop('columns')[1] + .render('conflict', { + kbnType: 'conflict', + conflictDescriptions: { keyword: ['index_a'], long: ['index_b'] }, + }) ); expect(tableCell).toMatchSnapshot(); }); @@ -163,4 +176,14 @@ describe('Table', () => { expect(renderFieldName(runtimeField)).toMatchSnapshot(); }); + + test('render conflict summary modal ', () => { + expect( + getConflictModalContent({ + closeFn: () => {}, + fieldName: 'message', + conflictDescriptions: { keyword: ['index_a'], long: ['index_b'] }, + }) + ).toMatchSnapshot(); + }); }); diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.tsx index 4fdecff842c3f..e4ff0e3152972 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.tsx @@ -7,6 +7,7 @@ */ import React, { PureComponent } from 'react'; +import { OverlayModalStart } from 'src/core/public'; import { EuiIcon, @@ -15,9 +16,19 @@ import { EuiBasicTableColumn, EuiBadge, EuiToolTip, + EuiModalHeader, + EuiModalFooter, + EuiModalBody, + EuiButton, + EuiModalHeaderTitle, + EuiText, + EuiBasicTable, + EuiCode, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { toMountPoint } from '../../../../../../../kibana_react/public'; import { IIndexPattern } from '../../../../../../../data/public'; import { IndexedFieldItem } from '../../types'; @@ -28,6 +39,11 @@ const additionalInfoAriaLabel = i18n.translate( { defaultMessage: 'Additional field information' } ); +const conflictDetailIconAria = i18n.translate( + 'indexPatternManagement.editIndexPattern.fields.table.conflictDetailIconAria', + { defaultMessage: 'Conflict Detail' } +); + const primaryTimeAriaLabel = i18n.translate( 'indexPatternManagement.editIndexPattern.fields.table.primaryTimeAriaLabel', { defaultMessage: 'Primary time field' } @@ -38,21 +54,6 @@ const primaryTimeTooltip = i18n.translate( { defaultMessage: 'This field represents the time that events occurred.' } ); -const multiTypeAriaLabel = i18n.translate( - 'indexPatternManagement.editIndexPattern.fields.table.multiTypeAria', - { - defaultMessage: 'Multiple type field', - } -); - -const multiTypeTooltip = i18n.translate( - 'indexPatternManagement.editIndexPattern.fields.table.multiTypeTooltip', - { - defaultMessage: - 'The type of this field changes across indices. It is unavailable for many analysis functions.', - } -); - const nameHeader = i18n.translate( 'indexPatternManagement.editIndexPattern.fields.table.nameHeader', { @@ -152,6 +153,11 @@ const deleteDescription = i18n.translate( { defaultMessage: 'Delete' } ); +const conflictType = i18n.translate( + 'indexPatternManagement.editDataView.fields.table.conflictType', + { defaultMessage: 'Conflict' } +); + const labelDescription = i18n.translate( 'indexPatternManagement.editIndexPattern.fields.table.customLabelTooltip', { defaultMessage: 'A custom label for the field.' } @@ -172,8 +178,21 @@ interface IndexedFieldProps { items: IndexedFieldItem[]; editField: (field: IndexedFieldItem) => void; deleteField: (fieldName: string) => void; + openModal: OverlayModalStart['open']; } +const getItems = (conflictDescriptions: IndexedFieldItem['conflictDescriptions']) => { + const typesAndIndices: Array<{ type: string; indices: string }> = []; + Object.keys(conflictDescriptions!).forEach((type) => { + // only show first 100 indices just incase the list is CRAZY long + typesAndIndices.push({ + type, + indices: conflictDescriptions![type].slice(0, 99).join(', '), + }); + }); + return typesAndIndices; +}; + export const renderFieldName = (field: IndexedFieldItem, timeFieldName?: string) => ( {field.name} @@ -223,28 +242,119 @@ export const renderFieldName = (field: IndexedFieldItem, timeFieldName?: string) ); +const conflictColumns = [ + { + field: 'type', + name: i18n.translate( + 'indexPatternManagement.editIndexPattern.fields.table.conflictModalTypeColumn', + { defaultMessage: 'Type' } + ), + }, + { + field: 'indices', + name: i18n.translate( + 'indexPatternManagement.editIndexPattern.fields.table.conflictModalIndicesColumn', + { defaultMessage: 'Indices' } + ), + }, +]; + +export const getConflictModalContent = ({ + closeFn, + fieldName, + conflictDescriptions, +}: { + closeFn: () => void; + fieldName: string; + conflictDescriptions: IndexedFieldItem['conflictDescriptions']; +}) => ( + <> + + +

+ +

+
+
+ + +

+ {fieldName} }} + /> +

+ +
+
+ + + + + + +); + +const getConflictBtn = ( + fieldName: string, + conflictDescriptions: IndexedFieldItem['conflictDescriptions'], + openModal: IndexedFieldProps['openModal'] +) => { + const onClick = () => { + const overlayRef = openModal( + toMountPoint( + getConflictModalContent({ + closeFn: () => { + overlayRef.close(); + }, + fieldName, + conflictDescriptions, + }) + ) + ); + }; + + return ( + + + {conflictType} + + + ); +}; + export class Table extends PureComponent { renderBooleanTemplate(value: string, arialLabel: string) { return value ? : ; } - renderFieldType(type: string, isConflict: boolean) { + renderFieldType(type: string, field: IndexedFieldItem) { return ( - {type} - {isConflict ? ( - -   - - - ) : ( - '' - )} + {type !== 'conflict' ? type : ''} + {field.conflictDescriptions + ? getConflictBtn(field.name, field.conflictDescriptions, this.props.openModal) + : ''} ); } @@ -275,7 +385,7 @@ export class Table extends PureComponent { dataType: 'string', sortable: true, render: (value: string, field: IndexedFieldItem) => { - return this.renderFieldType(value, field.kbnType === 'conflict'); + return this.renderFieldType(value, field); }, 'data-test-subj': 'indexedFieldType', }, diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx index b058ad4a7672c..1e0d36f465be5 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx @@ -7,7 +7,9 @@ */ import React, { Component } from 'react'; +import { i18n } from '@kbn/i18n'; import { createSelector } from 'reselect'; +import { OverlayStart } from 'src/core/public'; import { IndexPatternField, IndexPattern } from '../../../../../../plugins/data/public'; import { useKibana } from '../../../../../../plugins/kibana_react/public'; import { Table } from './components/table'; @@ -26,6 +28,7 @@ interface IndexedFieldsTableProps { }; fieldWildcardMatcher: (filters: any[]) => (val: any) => boolean; userEditPermission: boolean; + openModal: OverlayStart['openModal']; } interface IndexedFieldsTableState { @@ -65,12 +68,25 @@ class IndexedFields extends Component) => f.value); const fieldWildcardMatch = fieldWildcardMatcher(sourceFilters || []); + const getDisplayEsType = (arr: string[]): string => { + const length = arr.length; + if (length < 1) { + return ''; + } + if (length > 1) { + return i18n.translate('indexPatternManagement.editIndexPattern.fields.conflictType', { + defaultMessage: 'conflict', + }); + } + return arr[0]; + }; + return ( (fields && fields.map((field) => { return { ...field.spec, - type: field.esTypes?.join(', ') || '', + type: getDisplayEsType(field.esTypes || []), kbnType: field.type, displayName: field.displayName, format: indexPattern.getFormatterForFieldNoDefault(field.name)?.type?.title || '', @@ -119,6 +135,7 @@ class IndexedFields extends Component this.props.helpers.editField(field.name)} deleteField={(fieldName) => this.props.helpers.deleteField(fieldName)} + openModal={this.props.openModal} /> ); diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/tabs.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/tabs.tsx index 821f1d533e201..41ce0b7999500 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/tabs.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/tabs.tsx @@ -80,7 +80,7 @@ export function Tabs({ location, refreshFields, }: TabsProps) { - const { application, uiSettings, docLinks, indexPatternFieldEditor } = + const { application, uiSettings, docLinks, indexPatternFieldEditor, overlays } = useKibana().services; const [fieldFilter, setFieldFilter] = useState(''); const [indexedFieldTypeFilter, setIndexedFieldTypeFilter] = useState(''); @@ -93,6 +93,13 @@ export function Tabs({ const closeEditorHandler = useRef<() => void | undefined>(); const { DeleteRuntimeFieldProvider } = indexPatternFieldEditor; + const conflict = i18n.translate( + 'indexPatternManagement.editIndexPattern.fieldTypes.conflictType', + { + defaultMessage: 'conflict', + } + ); + const refreshFilters = useCallback(() => { const tempIndexedFieldTypes: string[] = []; const tempScriptedFieldLanguages: string[] = []; @@ -103,7 +110,7 @@ export function Tabs({ } } else { if (field.esTypes) { - tempIndexedFieldTypes.push(field.esTypes?.join(', ')); + tempIndexedFieldTypes.push(field.esTypes.length === 1 ? field.esTypes[0] : conflict); } } }); @@ -112,7 +119,7 @@ export function Tabs({ setScriptedFieldLanguages( convertToEuiSelectOption(tempScriptedFieldLanguages, 'scriptedFieldLanguages') ); - }, [indexPattern]); + }, [indexPattern, conflict]); const closeFieldEditor = useCallback(() => { if (closeEditorHandler.current) { @@ -230,6 +237,7 @@ export function Tabs({ deleteField, getFieldInfo, }} + openModal={overlays.openModal} /> )} @@ -288,6 +296,7 @@ export function Tabs({ openFieldEditor, DeleteRuntimeFieldProvider, refreshFields, + overlays, ] ); diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 4b1be26583eed..751964b47a05b 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -3351,8 +3351,6 @@ "indexPatternManagement.editIndexPattern.fields.table.isAggregatableAria": "は集約可能です", "indexPatternManagement.editIndexPattern.fields.table.isExcludedAria": "は除外されています", "indexPatternManagement.editIndexPattern.fields.table.isSearchableAria": "は検索可能です", - "indexPatternManagement.editIndexPattern.fields.table.multiTypeAria": "複数タイプのフィールド", - "indexPatternManagement.editIndexPattern.fields.table.multiTypeTooltip": "このフィールドのタイプはインデックスごとに変わります。多くの分析機能には使用できません。", "indexPatternManagement.editIndexPattern.fields.table.nameHeader": "名前", "indexPatternManagement.editIndexPattern.fields.table.primaryTimeAriaLabel": "プライマリ時間フィールド", "indexPatternManagement.editIndexPattern.fields.table.primaryTimeTooltip": "このフィールドはイベントの発生時刻を表します。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 3acc0c8921ffe..2878443e797c2 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -3374,8 +3374,6 @@ "indexPatternManagement.editIndexPattern.fields.table.isAggregatableAria": "可聚合", "indexPatternManagement.editIndexPattern.fields.table.isExcludedAria": "已排除", "indexPatternManagement.editIndexPattern.fields.table.isSearchableAria": "可搜索", - "indexPatternManagement.editIndexPattern.fields.table.multiTypeAria": "多类型字段", - "indexPatternManagement.editIndexPattern.fields.table.multiTypeTooltip": "此字段的类型在不同的索引中会有所不同。其不可用于许多分析功能。", "indexPatternManagement.editIndexPattern.fields.table.nameHeader": "名称", "indexPatternManagement.editIndexPattern.fields.table.primaryTimeAriaLabel": "主要时间字段", "indexPatternManagement.editIndexPattern.fields.table.primaryTimeTooltip": "此字段表示事件发生的时间。",