mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
allow filtering index pattern UI for runtime fields (#124114)
* allow filtering index pattern UI for runtime fields * fix namespace error * add multi select filters * add fix for failing tests * test fixes
This commit is contained in:
parent
ecff412e22
commit
01d479108b
12 changed files with 430 additions and 82 deletions
|
@ -141,6 +141,24 @@ exports[`IndexedFieldsTable IndexedFieldsTable with rollup index pattern should
|
|||
"name": "amount",
|
||||
"type": "long",
|
||||
},
|
||||
Object {
|
||||
"displayName": "runtime",
|
||||
"excluded": false,
|
||||
"format": "",
|
||||
"hasRuntime": true,
|
||||
"info": Array [],
|
||||
"isMapped": false,
|
||||
"isUserEditable": false,
|
||||
"kbnType": "number",
|
||||
"name": "runtime",
|
||||
"runtimeField": Object {
|
||||
"script": Object {
|
||||
"source": "emit('Hello');",
|
||||
},
|
||||
"type": "long",
|
||||
},
|
||||
"type": "long",
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
|
@ -182,6 +200,43 @@ exports[`IndexedFieldsTable should filter based on the query bar 1`] = `
|
|||
</div>
|
||||
`;
|
||||
|
||||
exports[`IndexedFieldsTable should filter based on the schema filter 1`] = `
|
||||
<div>
|
||||
<Table
|
||||
deleteField={[Function]}
|
||||
editField={[Function]}
|
||||
indexPattern={
|
||||
Object {
|
||||
"getFormatterForFieldNoDefault": [Function],
|
||||
"getNonScriptedFields": [Function],
|
||||
}
|
||||
}
|
||||
items={
|
||||
Array [
|
||||
Object {
|
||||
"displayName": "runtime",
|
||||
"excluded": false,
|
||||
"format": "",
|
||||
"hasRuntime": true,
|
||||
"info": Array [],
|
||||
"isMapped": false,
|
||||
"isUserEditable": false,
|
||||
"kbnType": "number",
|
||||
"name": "runtime",
|
||||
"runtimeField": Object {
|
||||
"script": Object {
|
||||
"source": "emit('Hello');",
|
||||
},
|
||||
"type": "long",
|
||||
},
|
||||
"type": "long",
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`IndexedFieldsTable should filter based on the type filter 1`] = `
|
||||
<div>
|
||||
<Table
|
||||
|
@ -291,6 +346,24 @@ exports[`IndexedFieldsTable should render normally 1`] = `
|
|||
"name": "amount",
|
||||
"type": "long",
|
||||
},
|
||||
Object {
|
||||
"displayName": "runtime",
|
||||
"excluded": false,
|
||||
"format": "",
|
||||
"hasRuntime": true,
|
||||
"info": Array [],
|
||||
"isMapped": false,
|
||||
"isUserEditable": false,
|
||||
"kbnType": "number",
|
||||
"name": "runtime",
|
||||
"runtimeField": Object {
|
||||
"script": Object {
|
||||
"source": "emit('Hello');",
|
||||
},
|
||||
"type": "long",
|
||||
},
|
||||
"type": "long",
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
|
|
|
@ -11,6 +11,7 @@ import { shallow, ShallowWrapper } from 'enzyme';
|
|||
import { DataViewField, DataView, DataViewType } from 'src/plugins/data_views/public';
|
||||
import { IndexedFieldsTable } from './indexed_fields_table';
|
||||
import { getFieldInfo } from '../../utils';
|
||||
import { RuntimeField } from 'src/plugins/data_views/common';
|
||||
|
||||
jest.mock('@elastic/eui', () => ({
|
||||
EuiFlexGroup: 'eui-flex-group',
|
||||
|
@ -67,11 +68,12 @@ const rollupIndexPattern = {
|
|||
} as unknown as DataView;
|
||||
|
||||
const mockFieldToIndexPatternField = (
|
||||
spec: Record<string, string | string[] | boolean | undefined>
|
||||
spec: Record<string, string | string[] | boolean | undefined | RuntimeField>
|
||||
) => {
|
||||
return new DataViewField(spec as unknown as DataViewField['spec']);
|
||||
};
|
||||
|
||||
const runtimeField: RuntimeField = { type: 'long', script: { source: "emit('Hello');" } };
|
||||
const fields = [
|
||||
{
|
||||
name: 'Elastic',
|
||||
|
@ -88,6 +90,11 @@ const fields = [
|
|||
isUserEditable: true,
|
||||
},
|
||||
{ name: 'amount', displayName: 'amount', esTypes: ['long'], isUserEditable: true },
|
||||
{
|
||||
name: 'runtime',
|
||||
displayName: 'runtime',
|
||||
runtimeField,
|
||||
},
|
||||
].map(mockFieldToIndexPatternField);
|
||||
|
||||
describe('IndexedFieldsTable', () => {
|
||||
|
@ -100,7 +107,8 @@ describe('IndexedFieldsTable', () => {
|
|||
fieldWildcardMatcher={() => {
|
||||
return () => false;
|
||||
}}
|
||||
indexedFieldTypeFilter=""
|
||||
indexedFieldTypeFilter={[]}
|
||||
schemaFieldTypeFilter={[]}
|
||||
fieldFilter=""
|
||||
/>
|
||||
).dive();
|
||||
|
@ -120,7 +128,8 @@ describe('IndexedFieldsTable', () => {
|
|||
fieldWildcardMatcher={() => {
|
||||
return () => false;
|
||||
}}
|
||||
indexedFieldTypeFilter=""
|
||||
indexedFieldTypeFilter={[]}
|
||||
schemaFieldTypeFilter={[]}
|
||||
fieldFilter=""
|
||||
/>
|
||||
).dive();
|
||||
|
@ -141,13 +150,36 @@ describe('IndexedFieldsTable', () => {
|
|||
fieldWildcardMatcher={() => {
|
||||
return () => false;
|
||||
}}
|
||||
indexedFieldTypeFilter=""
|
||||
indexedFieldTypeFilter={[]}
|
||||
schemaFieldTypeFilter={[]}
|
||||
fieldFilter=""
|
||||
/>
|
||||
).dive();
|
||||
|
||||
await new Promise((resolve) => process.nextTick(resolve));
|
||||
component.setProps({ indexedFieldTypeFilter: 'date' });
|
||||
component.setProps({ indexedFieldTypeFilter: ['date'] });
|
||||
component.update();
|
||||
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should filter based on the schema filter', async () => {
|
||||
const component: ShallowWrapper<any, Readonly<{}>, React.Component<{}, {}, any>> = shallow(
|
||||
<IndexedFieldsTable
|
||||
fields={fields}
|
||||
indexPattern={indexPattern}
|
||||
helpers={helpers}
|
||||
fieldWildcardMatcher={() => {
|
||||
return () => false;
|
||||
}}
|
||||
indexedFieldTypeFilter={[]}
|
||||
schemaFieldTypeFilter={[]}
|
||||
fieldFilter=""
|
||||
/>
|
||||
).dive();
|
||||
|
||||
await new Promise((resolve) => process.nextTick(resolve));
|
||||
component.setProps({ schemaFieldTypeFilter: ['runtime'] });
|
||||
component.update();
|
||||
|
||||
expect(component).toMatchSnapshot();
|
||||
|
@ -163,7 +195,8 @@ describe('IndexedFieldsTable', () => {
|
|||
fieldWildcardMatcher={() => {
|
||||
return () => false;
|
||||
}}
|
||||
indexedFieldTypeFilter=""
|
||||
indexedFieldTypeFilter={[]}
|
||||
schemaFieldTypeFilter={[]}
|
||||
fieldFilter=""
|
||||
/>
|
||||
).dive();
|
||||
|
|
|
@ -19,7 +19,8 @@ interface IndexedFieldsTableProps {
|
|||
fields: DataViewField[];
|
||||
indexPattern: DataView;
|
||||
fieldFilter?: string;
|
||||
indexedFieldTypeFilter?: string;
|
||||
indexedFieldTypeFilter: string[];
|
||||
schemaFieldTypeFilter: string[];
|
||||
helpers: {
|
||||
editField: (fieldName: string) => void;
|
||||
deleteField: (fieldName: string) => void;
|
||||
|
@ -93,7 +94,8 @@ class IndexedFields extends Component<IndexedFieldsTableProps, IndexedFieldsTabl
|
|||
(state: IndexedFieldsTableState, props: IndexedFieldsTableProps) => props.fieldFilter,
|
||||
(state: IndexedFieldsTableState, props: IndexedFieldsTableProps) =>
|
||||
props.indexedFieldTypeFilter,
|
||||
(fields, fieldFilter, indexedFieldTypeFilter) => {
|
||||
(state: IndexedFieldsTableState, props: IndexedFieldsTableProps) => props.schemaFieldTypeFilter,
|
||||
(fields, fieldFilter, indexedFieldTypeFilter, schemaFieldTypeFilter) => {
|
||||
if (fieldFilter) {
|
||||
const normalizedFieldFilter = fieldFilter.toLowerCase();
|
||||
fields = fields.filter(
|
||||
|
@ -103,14 +105,34 @@ class IndexedFields extends Component<IndexedFieldsTableProps, IndexedFieldsTabl
|
|||
);
|
||||
}
|
||||
|
||||
if (indexedFieldTypeFilter) {
|
||||
if (indexedFieldTypeFilter.length) {
|
||||
// match conflict fields
|
||||
fields = fields.filter((field) => {
|
||||
if (indexedFieldTypeFilter === 'conflict' && field.kbnType === 'conflict') {
|
||||
if (indexedFieldTypeFilter.includes('conflict') && field.kbnType === 'conflict') {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
'runtimeField' in field &&
|
||||
field.runtimeField?.type &&
|
||||
indexedFieldTypeFilter.includes(field.runtimeField?.type)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
// match one of multiple types on a field
|
||||
return field.esTypes?.length && field.esTypes?.indexOf(indexedFieldTypeFilter) !== -1;
|
||||
return (
|
||||
field.esTypes?.length &&
|
||||
field.esTypes.filter((val) => indexedFieldTypeFilter.includes(val)).length
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
if (schemaFieldTypeFilter.length) {
|
||||
// match fields of schema type
|
||||
fields = fields.filter((field) => {
|
||||
return (
|
||||
(schemaFieldTypeFilter.includes('runtime') && 'runtimeField' in field) ||
|
||||
(schemaFieldTypeFilter.includes('indexed') && !('runtimeField' in field))
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -68,6 +68,7 @@ describe('ScriptedFieldsTable', () => {
|
|||
helpers={helpers}
|
||||
painlessDocLink={'painlessDoc'}
|
||||
saveIndexPattern={async () => {}}
|
||||
scriptedFieldLanguageFilter={[]}
|
||||
/>
|
||||
).dive();
|
||||
|
||||
|
@ -86,6 +87,7 @@ describe('ScriptedFieldsTable', () => {
|
|||
helpers={helpers}
|
||||
painlessDocLink={'painlessDoc'}
|
||||
saveIndexPattern={async () => {}}
|
||||
scriptedFieldLanguageFilter={[]}
|
||||
/>
|
||||
).dive();
|
||||
|
||||
|
@ -117,6 +119,7 @@ describe('ScriptedFieldsTable', () => {
|
|||
painlessDocLink={'painlessDoc'}
|
||||
helpers={helpers}
|
||||
saveIndexPattern={async () => {}}
|
||||
scriptedFieldLanguageFilter={[]}
|
||||
/>
|
||||
).dive();
|
||||
|
||||
|
@ -125,7 +128,7 @@ describe('ScriptedFieldsTable', () => {
|
|||
await component.update(); // Fire `componentWillMount()`
|
||||
await component.update(); // Force update the component post async actions
|
||||
|
||||
component.setProps({ scriptedFieldLanguageFilter: 'painless' });
|
||||
component.setProps({ scriptedFieldLanguageFilter: ['painless'] });
|
||||
component.update();
|
||||
|
||||
expect(component).toMatchSnapshot();
|
||||
|
@ -142,6 +145,7 @@ describe('ScriptedFieldsTable', () => {
|
|||
painlessDocLink={'painlessDoc'}
|
||||
helpers={helpers}
|
||||
saveIndexPattern={async () => {}}
|
||||
scriptedFieldLanguageFilter={[]}
|
||||
/>
|
||||
).dive();
|
||||
|
||||
|
@ -162,6 +166,7 @@ describe('ScriptedFieldsTable', () => {
|
|||
helpers={helpers}
|
||||
painlessDocLink={'painlessDoc'}
|
||||
saveIndexPattern={async () => {}}
|
||||
scriptedFieldLanguageFilter={[]}
|
||||
/>
|
||||
).dive();
|
||||
|
||||
|
@ -189,6 +194,7 @@ describe('ScriptedFieldsTable', () => {
|
|||
helpers={helpers}
|
||||
painlessDocLink={'painlessDoc'}
|
||||
saveIndexPattern={async () => {}}
|
||||
scriptedFieldLanguageFilter={[]}
|
||||
/>
|
||||
).dive();
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ import { useKibana } from '../../../../../../plugins/kibana_react/public';
|
|||
interface ScriptedFieldsTableProps {
|
||||
indexPattern: DataView;
|
||||
fieldFilter?: string;
|
||||
scriptedFieldLanguageFilter?: string;
|
||||
scriptedFieldLanguageFilter: string[];
|
||||
helpers: {
|
||||
redirectToRoute: Function;
|
||||
getRouteHref?: Function;
|
||||
|
@ -92,9 +92,9 @@ class ScriptedFields extends Component<ScriptedFieldsTableProps, ScriptedFieldsT
|
|||
|
||||
let languageFilteredFields = fields;
|
||||
|
||||
if (scriptedFieldLanguageFilter) {
|
||||
languageFilteredFields = fields.filter(
|
||||
(field) => field.lang === this.props.scriptedFieldLanguageFilter
|
||||
if (scriptedFieldLanguageFilter.length) {
|
||||
languageFilteredFields = fields.filter((field) =>
|
||||
scriptedFieldLanguageFilter.includes(field.lang)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,15 +9,18 @@
|
|||
import React, { useState, useCallback, useEffect, Fragment, useMemo, useRef } from 'react';
|
||||
import { RouteComponentProps } from 'react-router-dom';
|
||||
import {
|
||||
EuiFilterButton,
|
||||
EuiFilterGroup,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiPopover,
|
||||
EuiTabbedContent,
|
||||
EuiTabbedContentTab,
|
||||
EuiSpacer,
|
||||
EuiFieldSearch,
|
||||
EuiSelect,
|
||||
EuiSelectOption,
|
||||
EuiButton,
|
||||
EuiFilterSelectItem,
|
||||
FilterChecked,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { fieldWildcardMatcher } from '../../../../../kibana_utils/public';
|
||||
|
@ -34,7 +37,7 @@ import { TAB_INDEXED_FIELDS, TAB_SCRIPTED_FIELDS, TAB_SOURCE_FILTERS } from '../
|
|||
import { SourceFiltersTable } from '../source_filters_table';
|
||||
import { IndexedFieldsTable } from '../indexed_fields_table';
|
||||
import { ScriptedFieldsTable } from '../scripted_fields_table';
|
||||
import { getTabs, getPath, convertToEuiSelectOption } from './utils';
|
||||
import { getTabs, getPath, convertToEuiFilterOptions } from './utils';
|
||||
import { getFieldInfo } from '../../utils';
|
||||
|
||||
interface TabsProps extends Pick<RouteComponentProps, 'history' | 'location'> {
|
||||
|
@ -44,6 +47,12 @@ interface TabsProps extends Pick<RouteComponentProps, 'history' | 'location'> {
|
|||
refreshFields: () => void;
|
||||
}
|
||||
|
||||
interface FilterItems {
|
||||
value: string;
|
||||
name: string;
|
||||
checked?: FilterChecked;
|
||||
}
|
||||
|
||||
const searchAriaLabel = i18n.translate(
|
||||
'indexPatternManagement.editIndexPattern.fields.searchAria',
|
||||
{
|
||||
|
@ -51,6 +60,10 @@ const searchAriaLabel = i18n.translate(
|
|||
}
|
||||
);
|
||||
|
||||
const filterLabel = i18n.translate('indexPatternManagement.editIndexPattern.fields.filter', {
|
||||
defaultMessage: 'Field type',
|
||||
});
|
||||
|
||||
const filterAriaLabel = i18n.translate(
|
||||
'indexPatternManagement.editIndexPattern.fields.filterAria',
|
||||
{
|
||||
|
@ -58,6 +71,45 @@ const filterAriaLabel = i18n.translate(
|
|||
}
|
||||
);
|
||||
|
||||
const schemaFilterLabel = i18n.translate('indexPatternManagement.editIndexPattern.fields.schema', {
|
||||
defaultMessage: 'Schema type',
|
||||
});
|
||||
|
||||
const schemaAriaLabel = i18n.translate(
|
||||
'indexPatternManagement.editIndexPattern.fields.schemaAria',
|
||||
{
|
||||
defaultMessage: 'Filter schema types',
|
||||
}
|
||||
);
|
||||
|
||||
const scriptedFieldFilterLabel = i18n.translate(
|
||||
'indexPatternManagement.editIndexPattern.fields.scriptedFieldFilter',
|
||||
{
|
||||
defaultMessage: 'All languages',
|
||||
}
|
||||
);
|
||||
|
||||
const scriptedFieldAriaLabel = i18n.translate(
|
||||
'indexPatternManagement.editIndexPattern.fields.scriptedFieldFilterAria',
|
||||
{
|
||||
defaultMessage: 'Filter scripted field languages',
|
||||
}
|
||||
);
|
||||
|
||||
const schemaOptionRuntime = i18n.translate(
|
||||
'indexPatternManagement.editIndexPattern.fields.runtime',
|
||||
{
|
||||
defaultMessage: 'Runtime',
|
||||
}
|
||||
);
|
||||
|
||||
const schemaOptionIndexed = i18n.translate(
|
||||
'indexPatternManagement.editIndexPattern.fields.indexed',
|
||||
{
|
||||
defaultMessage: 'Indexed',
|
||||
}
|
||||
);
|
||||
|
||||
const filterPlaceholder = i18n.translate(
|
||||
'indexPatternManagement.editIndexPattern.fields.filterPlaceholder',
|
||||
{
|
||||
|
@ -83,16 +135,53 @@ export function Tabs({
|
|||
const { application, uiSettings, docLinks, dataViewFieldEditor, overlays, theme } =
|
||||
useKibana<IndexPatternManagmentContext>().services;
|
||||
const [fieldFilter, setFieldFilter] = useState<string>('');
|
||||
const [indexedFieldTypeFilter, setIndexedFieldTypeFilter] = useState<string>('');
|
||||
const [scriptedFieldLanguageFilter, setScriptedFieldLanguageFilter] = useState<string>('');
|
||||
const [indexedFieldTypes, setIndexedFieldType] = useState<EuiSelectOption[]>([]);
|
||||
const [scriptedFieldLanguages, setScriptedFieldLanguages] = useState<EuiSelectOption[]>([]);
|
||||
const [syncingStateFunc, setSyncingStateFunc] = useState<any>({
|
||||
getCurrentTab: () => TAB_INDEXED_FIELDS,
|
||||
});
|
||||
const [scriptedFieldLanguageFilter, setScriptedFieldLanguageFilter] = useState<string[]>([]);
|
||||
const [isScriptedFieldFilterOpen, setIsScriptedFieldFilterOpen] = useState(false);
|
||||
const [scriptedFieldLanguages, setScriptedFieldLanguages] = useState<FilterItems[]>([]);
|
||||
const [indexedFieldTypeFilter, setIndexedFieldTypeFilter] = useState<string[]>([]);
|
||||
const [isIndexedFilterOpen, setIsIndexedFilterOpen] = useState(false);
|
||||
const [indexedFieldTypes, setIndexedFieldTypes] = useState<FilterItems[]>([]);
|
||||
const [schemaFieldTypeFilter, setSchemaFieldTypeFilter] = useState<string[]>([]);
|
||||
const [isSchemaFilterOpen, setIsSchemaFilterOpen] = useState(false);
|
||||
const [schemaItems, setSchemaItems] = useState<FilterItems[]>([
|
||||
{
|
||||
value: 'runtime',
|
||||
name: schemaOptionRuntime,
|
||||
},
|
||||
{
|
||||
value: 'indexed',
|
||||
name: schemaOptionIndexed,
|
||||
},
|
||||
]);
|
||||
const closeEditorHandler = useRef<() => void | undefined>();
|
||||
const { DeleteRuntimeFieldProvider } = dataViewFieldEditor;
|
||||
|
||||
const updateFilterItem = (
|
||||
items: FilterItems[],
|
||||
index: number,
|
||||
updater: (a: FilterItems[]) => void
|
||||
) => {
|
||||
if (!items[index]) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newItems = [...items];
|
||||
|
||||
switch (newItems[index].checked) {
|
||||
case 'on':
|
||||
newItems[index].checked = undefined;
|
||||
break;
|
||||
|
||||
default:
|
||||
newItems[index].checked = 'on';
|
||||
}
|
||||
|
||||
updater(newItems);
|
||||
};
|
||||
|
||||
const refreshFilters = useCallback(() => {
|
||||
const tempIndexedFieldTypes: string[] = [];
|
||||
const tempScriptedFieldLanguages: string[] = [];
|
||||
|
@ -113,10 +202,8 @@ export function Tabs({
|
|||
}
|
||||
});
|
||||
|
||||
setIndexedFieldType(convertToEuiSelectOption(tempIndexedFieldTypes, 'indexedFiledTypes'));
|
||||
setScriptedFieldLanguages(
|
||||
convertToEuiSelectOption(tempScriptedFieldLanguages, 'scriptedFieldLanguages')
|
||||
);
|
||||
setIndexedFieldTypes(convertToEuiFilterOptions(tempIndexedFieldTypes));
|
||||
setScriptedFieldLanguages(convertToEuiFilterOptions(tempScriptedFieldLanguages));
|
||||
}, [indexPattern]);
|
||||
|
||||
const closeFieldEditor = useCallback(() => {
|
||||
|
@ -172,13 +259,92 @@ export function Tabs({
|
|||
{type === TAB_INDEXED_FIELDS && indexedFieldTypes.length > 0 && (
|
||||
<>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiSelect
|
||||
options={indexedFieldTypes}
|
||||
value={indexedFieldTypeFilter}
|
||||
onChange={(e) => setIndexedFieldTypeFilter(e.target.value)}
|
||||
data-test-subj="indexedFieldTypeFilterDropdown"
|
||||
aria-label={filterAriaLabel}
|
||||
/>
|
||||
<EuiFilterGroup>
|
||||
<EuiPopover
|
||||
anchorPosition="downCenter"
|
||||
data-test-subj="indexedFieldTypeFilterDropdown-popover"
|
||||
button={
|
||||
<EuiFilterButton
|
||||
aria-label={filterAriaLabel}
|
||||
data-test-subj="indexedFieldTypeFilterDropdown"
|
||||
iconType="arrowDown"
|
||||
onClick={() => setIsIndexedFilterOpen(!isIndexedFilterOpen)}
|
||||
isSelected={isIndexedFilterOpen}
|
||||
numFilters={indexedFieldTypes.length}
|
||||
hasActiveFilters={!!indexedFieldTypes.find((item) => item.checked === 'on')}
|
||||
numActiveFilters={
|
||||
indexedFieldTypes.filter((item) => item.checked === 'on').length
|
||||
}
|
||||
>
|
||||
{filterLabel}
|
||||
</EuiFilterButton>
|
||||
}
|
||||
isOpen={isIndexedFilterOpen}
|
||||
closePopover={() => setIsIndexedFilterOpen(false)}
|
||||
>
|
||||
{indexedFieldTypes.map((item, index) => (
|
||||
<EuiFilterSelectItem
|
||||
checked={item.checked}
|
||||
key={item.value}
|
||||
onClick={() => {
|
||||
setIndexedFieldTypeFilter(
|
||||
item.checked
|
||||
? indexedFieldTypeFilter.filter((f) => f !== item.value)
|
||||
: [...indexedFieldTypeFilter, item.value]
|
||||
);
|
||||
updateFilterItem(indexedFieldTypes, index, setIndexedFieldTypes);
|
||||
}}
|
||||
data-test-subj={`indexedFieldTypeFilterDropdown-option-${item.value}${
|
||||
item.checked ? '-checked' : ''
|
||||
}`}
|
||||
>
|
||||
{item.name}
|
||||
</EuiFilterSelectItem>
|
||||
))}
|
||||
</EuiPopover>
|
||||
<EuiPopover
|
||||
anchorPosition="downCenter"
|
||||
data-test-subj="schemaFieldTypeFilterDropdown-popover"
|
||||
button={
|
||||
<EuiFilterButton
|
||||
aria-label={schemaAriaLabel}
|
||||
data-test-subj="schemaFieldTypeFilterDropdown"
|
||||
iconType="arrowDown"
|
||||
onClick={() => setIsSchemaFilterOpen(!isSchemaFilterOpen)}
|
||||
isSelected={isSchemaFilterOpen}
|
||||
numFilters={schemaItems.length}
|
||||
hasActiveFilters={!!schemaItems.find((item) => item.checked === 'on')}
|
||||
numActiveFilters={
|
||||
schemaItems.filter((item) => item.checked === 'on').length
|
||||
}
|
||||
>
|
||||
{schemaFilterLabel}
|
||||
</EuiFilterButton>
|
||||
}
|
||||
isOpen={isSchemaFilterOpen}
|
||||
closePopover={() => setIsSchemaFilterOpen(false)}
|
||||
>
|
||||
{schemaItems.map((item, index) => (
|
||||
<EuiFilterSelectItem
|
||||
checked={item.checked}
|
||||
key={item.value}
|
||||
onClick={() => {
|
||||
setSchemaFieldTypeFilter(
|
||||
item.checked
|
||||
? schemaFieldTypeFilter.filter((f) => f !== item.value)
|
||||
: [...schemaFieldTypeFilter, item.value]
|
||||
);
|
||||
updateFilterItem(schemaItems, index, setSchemaItems);
|
||||
}}
|
||||
data-test-subj={`schemaFieldTypeFilterDropdown-option-${item.value}${
|
||||
item.checked ? '-checked' : ''
|
||||
}`}
|
||||
>
|
||||
{item.name}
|
||||
</EuiFilterSelectItem>
|
||||
))}
|
||||
</EuiPopover>
|
||||
</EuiFilterGroup>
|
||||
</EuiFlexItem>
|
||||
{userEditPermission && (
|
||||
<EuiFlexItem grow={false}>
|
||||
|
@ -191,12 +357,52 @@ export function Tabs({
|
|||
)}
|
||||
{type === TAB_SCRIPTED_FIELDS && scriptedFieldLanguages.length > 0 && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiSelect
|
||||
options={scriptedFieldLanguages}
|
||||
value={scriptedFieldLanguageFilter}
|
||||
onChange={(e) => setScriptedFieldLanguageFilter(e.target.value)}
|
||||
data-test-subj="scriptedFieldLanguageFilterDropdown"
|
||||
/>
|
||||
<EuiFilterGroup>
|
||||
<EuiPopover
|
||||
anchorPosition="downCenter"
|
||||
data-test-subj="scriptedFieldLanguageFilterDropdown-popover"
|
||||
button={
|
||||
<EuiFilterButton
|
||||
aria-label={scriptedFieldAriaLabel}
|
||||
data-test-subj="scriptedFieldLanguageFilterDropdown"
|
||||
iconType="arrowDown"
|
||||
onClick={() => setIsScriptedFieldFilterOpen(!isScriptedFieldFilterOpen)}
|
||||
isSelected={isScriptedFieldFilterOpen}
|
||||
numFilters={scriptedFieldLanguages.length}
|
||||
hasActiveFilters={
|
||||
!!scriptedFieldLanguages.find((item) => item.checked === 'on')
|
||||
}
|
||||
numActiveFilters={
|
||||
scriptedFieldLanguages.filter((item) => item.checked === 'on').length
|
||||
}
|
||||
>
|
||||
{scriptedFieldFilterLabel}
|
||||
</EuiFilterButton>
|
||||
}
|
||||
isOpen={isScriptedFieldFilterOpen}
|
||||
closePopover={() => setIsScriptedFieldFilterOpen(false)}
|
||||
>
|
||||
{scriptedFieldLanguages.map((item, index) => (
|
||||
<EuiFilterSelectItem
|
||||
checked={item.checked}
|
||||
key={item.value}
|
||||
onClick={() => {
|
||||
setScriptedFieldLanguageFilter(
|
||||
item.checked
|
||||
? scriptedFieldLanguageFilter.filter((f) => f !== item.value)
|
||||
: [...scriptedFieldLanguageFilter, item.value]
|
||||
);
|
||||
updateFilterItem(scriptedFieldLanguages, index, setScriptedFieldLanguages);
|
||||
}}
|
||||
data-test-subj={`scriptedFieldLanguageFilterDropdown-option-${item.value}${
|
||||
item.checked ? '-checked' : ''
|
||||
}`}
|
||||
>
|
||||
{item.name}
|
||||
</EuiFilterSelectItem>
|
||||
))}
|
||||
</EuiPopover>
|
||||
</EuiFilterGroup>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
|
@ -206,8 +412,13 @@ export function Tabs({
|
|||
fieldFilter,
|
||||
indexedFieldTypeFilter,
|
||||
indexedFieldTypes,
|
||||
isIndexedFilterOpen,
|
||||
scriptedFieldLanguageFilter,
|
||||
scriptedFieldLanguages,
|
||||
isScriptedFieldFilterOpen,
|
||||
schemaItems,
|
||||
schemaFieldTypeFilter,
|
||||
isSchemaFilterOpen,
|
||||
openFieldEditor,
|
||||
userEditPermission,
|
||||
]
|
||||
|
@ -230,6 +441,7 @@ export function Tabs({
|
|||
fieldFilter={fieldFilter}
|
||||
fieldWildcardMatcher={fieldWildcardMatcherDecorated}
|
||||
indexedFieldTypeFilter={indexedFieldTypeFilter}
|
||||
schemaFieldTypeFilter={schemaFieldTypeFilter}
|
||||
helpers={{
|
||||
editField: openFieldEditor,
|
||||
deleteField,
|
||||
|
@ -289,6 +501,7 @@ export function Tabs({
|
|||
history,
|
||||
indexPattern,
|
||||
indexedFieldTypeFilter,
|
||||
schemaFieldTypeFilter,
|
||||
refreshFilters,
|
||||
scriptedFieldLanguageFilter,
|
||||
saveIndexPattern,
|
||||
|
|
|
@ -105,36 +105,11 @@ export function getPath(field: DataViewField, indexPattern: DataView) {
|
|||
return `/dataView/${indexPattern?.id}/field/${encodeURIComponent(field.name)}`;
|
||||
}
|
||||
|
||||
const allTypesDropDown = i18n.translate(
|
||||
'indexPatternManagement.editIndexPattern.fields.allTypesDropDown',
|
||||
{
|
||||
defaultMessage: 'All field types',
|
||||
}
|
||||
);
|
||||
|
||||
const allLangsDropDown = i18n.translate(
|
||||
'indexPatternManagement.editIndexPattern.fields.allLangsDropDown',
|
||||
{
|
||||
defaultMessage: 'All languages',
|
||||
}
|
||||
);
|
||||
|
||||
export function convertToEuiSelectOption(options: string[], type: string) {
|
||||
const euiOptions =
|
||||
options.length > 0
|
||||
? [
|
||||
{
|
||||
value: '',
|
||||
text: type === 'scriptedFieldLanguages' ? allLangsDropDown : allTypesDropDown,
|
||||
},
|
||||
]
|
||||
: [];
|
||||
return euiOptions.concat(
|
||||
uniq(options).map((option) => {
|
||||
return {
|
||||
value: option,
|
||||
text: option,
|
||||
};
|
||||
})
|
||||
);
|
||||
export function convertToEuiFilterOptions(options: string[]) {
|
||||
return uniq(options).map((option) => {
|
||||
return {
|
||||
value: option,
|
||||
name: option,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ export default function ({ getService, getPageObjects }) {
|
|||
expect(fieldType).to.be('keyword');
|
||||
}
|
||||
});
|
||||
await PageObjects.settings.clearFieldTypeFilter('keyword');
|
||||
|
||||
await PageObjects.settings.setFieldTypeFilter('long');
|
||||
|
||||
|
@ -54,6 +55,7 @@ export default function ({ getService, getPageObjects }) {
|
|||
expect(fieldType).to.be('long');
|
||||
}
|
||||
});
|
||||
await PageObjects.settings.clearFieldTypeFilter('long');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -66,6 +66,7 @@ export default function ({ getService, getPageObjects }) {
|
|||
expect(lang).to.be('painless');
|
||||
}
|
||||
});
|
||||
await PageObjects.settings.clearScriptedFieldLanguageFilter('painless');
|
||||
|
||||
await PageObjects.settings.setScriptedFieldLanguageFilter('expression');
|
||||
|
||||
|
@ -76,6 +77,7 @@ export default function ({ getService, getPageObjects }) {
|
|||
expect(lang).to.be('expression');
|
||||
}
|
||||
});
|
||||
await PageObjects.settings.clearScriptedFieldLanguageFilter('expression');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -258,18 +258,44 @@ export class SettingsPageObject extends FtrService {
|
|||
);
|
||||
}
|
||||
|
||||
async clearFieldTypeFilter(type: string) {
|
||||
await this.testSubjects.clickWhenNotDisabled('indexedFieldTypeFilterDropdown');
|
||||
await this.testSubjects.existOrFail('indexedFieldTypeFilterDropdown-popover');
|
||||
await this.testSubjects.existOrFail(`indexedFieldTypeFilterDropdown-option-${type}-checked`);
|
||||
await this.testSubjects.click(`indexedFieldTypeFilterDropdown-option-${type}-checked`);
|
||||
await this.testSubjects.existOrFail(`indexedFieldTypeFilterDropdown-option-${type}`);
|
||||
await this.browser.pressKeys(this.browser.keys.ESCAPE);
|
||||
}
|
||||
|
||||
async setFieldTypeFilter(type: string) {
|
||||
await this.find.clickByCssSelector(
|
||||
'select[data-test-subj="indexedFieldTypeFilterDropdown"] > option[value="' + type + '"]'
|
||||
await this.testSubjects.clickWhenNotDisabled('indexedFieldTypeFilterDropdown');
|
||||
await this.testSubjects.existOrFail('indexedFieldTypeFilterDropdown-popover');
|
||||
await this.testSubjects.existOrFail(`indexedFieldTypeFilterDropdown-option-${type}`);
|
||||
await this.testSubjects.click(`indexedFieldTypeFilterDropdown-option-${type}`);
|
||||
await this.testSubjects.existOrFail(`indexedFieldTypeFilterDropdown-option-${type}-checked`);
|
||||
await this.browser.pressKeys(this.browser.keys.ESCAPE);
|
||||
}
|
||||
|
||||
async clearScriptedFieldLanguageFilter(type: string) {
|
||||
await this.testSubjects.clickWhenNotDisabled('scriptedFieldLanguageFilterDropdown');
|
||||
await this.testSubjects.existOrFail('scriptedFieldLanguageFilterDropdown-popover');
|
||||
await this.testSubjects.existOrFail(
|
||||
`scriptedFieldLanguageFilterDropdown-option-${type}-checked`
|
||||
);
|
||||
await this.testSubjects.click(`scriptedFieldLanguageFilterDropdown-option-${type}-checked`);
|
||||
await this.testSubjects.existOrFail(`scriptedFieldLanguageFilterDropdown-option-${type}`);
|
||||
await this.browser.pressKeys(this.browser.keys.ESCAPE);
|
||||
}
|
||||
|
||||
async setScriptedFieldLanguageFilter(language: string) {
|
||||
await this.find.clickByCssSelector(
|
||||
'select[data-test-subj="scriptedFieldLanguageFilterDropdown"] > option[value="' +
|
||||
language +
|
||||
'"]'
|
||||
await this.testSubjects.clickWhenNotDisabled('scriptedFieldLanguageFilterDropdown');
|
||||
await this.testSubjects.existOrFail('scriptedFieldLanguageFilterDropdown-popover');
|
||||
await this.testSubjects.existOrFail(`scriptedFieldLanguageFilterDropdown-option-${language}`);
|
||||
await this.testSubjects.click(`scriptedFieldLanguageFilterDropdown-option-${language}`);
|
||||
await this.testSubjects.existOrFail(
|
||||
`scriptedFieldLanguageFilterDropdown-option-${language}-checked`
|
||||
);
|
||||
await this.browser.pressKeys(this.browser.keys.ESCAPE);
|
||||
}
|
||||
|
||||
async filterField(name: string) {
|
||||
|
|
|
@ -4143,8 +4143,6 @@
|
|||
"indexPatternManagement.editIndexPattern.deleteButton": "削除",
|
||||
"indexPatternManagement.editIndexPattern.deprecation": "スクリプトフィールドは廃止予定です。代わりに{runtimeDocs}を使用してください。",
|
||||
"indexPatternManagement.editIndexPattern.fields.addFieldButtonLabel": "フィールドの追加",
|
||||
"indexPatternManagement.editIndexPattern.fields.allLangsDropDown": "すべての言語",
|
||||
"indexPatternManagement.editIndexPattern.fields.allTypesDropDown": "すべてのフィールドタイプ",
|
||||
"indexPatternManagement.editIndexPattern.fields.conflictModal.closeBtn": "閉じる",
|
||||
"indexPatternManagement.editIndexPattern.fields.conflictModal.description": "{fieldName}フィールドの型がインデックス全体で変更され、検索、視覚化、他の分析で使用できない可能性があります。",
|
||||
"indexPatternManagement.editIndexPattern.fields.conflictModal.title": "このフィールドは型が競合しています",
|
||||
|
|
|
@ -3933,8 +3933,6 @@
|
|||
"indexPatternManagement.editIndexPattern.deleteButton": "删除",
|
||||
"indexPatternManagement.editIndexPattern.deprecation": "脚本字段已弃用。改用 {runtimeDocs}。",
|
||||
"indexPatternManagement.editIndexPattern.fields.addFieldButtonLabel": "添加字段",
|
||||
"indexPatternManagement.editIndexPattern.fields.allLangsDropDown": "所有语言",
|
||||
"indexPatternManagement.editIndexPattern.fields.allTypesDropDown": "所有字段类型",
|
||||
"indexPatternManagement.editIndexPattern.fields.conflictModal.closeBtn": "关闭",
|
||||
"indexPatternManagement.editIndexPattern.fields.conflictModal.description": "{fieldName} 字段的类型在不同索引中会有所不同,并且可能无法用于搜索、可视化和其他分析。",
|
||||
"indexPatternManagement.editIndexPattern.fields.conflictModal.title": "此字段存在类型冲突",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue