diff --git a/src/plugins/unified_field_list/public/components/field_item_button/__snapshots__/field_item_button.test.tsx.snap b/src/plugins/unified_field_list/public/components/field_item_button/__snapshots__/field_item_button.test.tsx.snap
index d1068660c1eaa..899d6a7579123 100644
--- a/src/plugins/unified_field_list/public/components/field_item_button/__snapshots__/field_item_button.test.tsx.snap
+++ b/src/plugins/unified_field_list/public/components/field_item_button/__snapshots__/field_item_button.test.tsx.snap
@@ -88,6 +88,35 @@ exports[`UnifiedFieldList renders properly for text-based co
/>
`;
+exports[`UnifiedFieldList renders properly for wildcard search 1`] = `
+
+ }
+ fieldName={
+
+ script date
+
+ }
+ isActive={false}
+ key="field-item-button-script date"
+ size="s"
+/>
+`;
+
exports[`UnifiedFieldList renders properly when a conflict field 1`] = `
', () => {
);
expect(component).toMatchSnapshot();
});
+
+ test('renders properly for wildcard search', () => {
+ const component = shallow(
+
+ );
+ expect(component).toMatchSnapshot();
+ });
});
diff --git a/src/plugins/unified_field_list/public/components/field_item_button/field_item_button.tsx b/src/plugins/unified_field_list/public/components/field_item_button/field_item_button.tsx
index df24125027e2f..ec0078431a8bc 100644
--- a/src/plugins/unified_field_list/public/components/field_item_button/field_item_button.tsx
+++ b/src/plugins/unified_field_list/public/components/field_item_button/field_item_button.tsx
@@ -14,6 +14,7 @@ import { EuiButtonIcon, EuiButtonIconProps, EuiHighlight, EuiIcon, EuiToolTip }
import type { DataViewField } from '@kbn/data-views-plugin/common';
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';
/**
@@ -194,7 +195,7 @@ export function FieldItemButton({
fieldIcon={}
fieldName={
@@ -230,3 +231,15 @@ function FieldConflictInfoIcon() {
);
}
+
+function getSearchHighlight(displayName: string, fieldSearchHighlight?: string): string {
+ const searchHighlight = fieldSearchHighlight || '';
+ if (
+ searchHighlight.includes('*') &&
+ fieldNameWildcardMatcher({ name: displayName }, searchHighlight)
+ ) {
+ return displayName;
+ }
+
+ return searchHighlight;
+}
diff --git a/src/plugins/unified_field_list/public/hooks/use_field_filters.test.tsx b/src/plugins/unified_field_list/public/hooks/use_field_filters.test.tsx
index 77327fcbee402..1295d14f374b2 100644
--- a/src/plugins/unified_field_list/public/hooks/use_field_filters.test.tsx
+++ b/src/plugins/unified_field_list/public/hooks/use_field_filters.test.tsx
@@ -68,6 +68,33 @@ describe('UnifiedFieldList useFieldFilters()', () => {
expect(result.current.onFilterField!(dataView.getFieldByName('bytes')!)).toBe(false);
});
+ it('should update correctly on search by name which has a wildcard', async () => {
+ const props: FieldFiltersParams = {
+ allFields: dataView.fields,
+ services: mockedServices,
+ };
+ const { result } = renderHook(useFieldFilters, {
+ initialProps: props,
+ });
+
+ expect(result.current.fieldSearchHighlight).toBe('');
+ expect(result.current.onFilterField).toBeUndefined();
+
+ act(() => {
+ result.current.fieldListFiltersProps.onChangeNameFilter('message*me1');
+ });
+
+ expect(result.current.fieldSearchHighlight).toBe('message*me1');
+ expect(result.current.onFilterField).toBeDefined();
+ expect(result.current.onFilterField!({ displayName: 'test' } as DataViewField)).toBe(false);
+ expect(result.current.onFilterField!({ displayName: 'message' } as DataViewField)).toBe(false);
+ expect(result.current.onFilterField!({ displayName: 'message.name1' } as DataViewField)).toBe(
+ true
+ );
+ expect(result.current.onFilterField!({ name: 'messagename10' } as DataViewField)).toBe(false);
+ expect(result.current.onFilterField!({ name: 'message.test' } as DataViewField)).toBe(false);
+ });
+
it('should update correctly on filter by type', async () => {
const props: FieldFiltersParams = {
allFields: dataView.fields,
diff --git a/src/plugins/unified_field_list/public/hooks/use_field_filters.ts b/src/plugins/unified_field_list/public/hooks/use_field_filters.ts
index 803739caba7c6..c3e08ff335602 100644
--- a/src/plugins/unified_field_list/public/hooks/use_field_filters.ts
+++ b/src/plugins/unified_field_list/public/hooks/use_field_filters.ts
@@ -13,6 +13,7 @@ import type { CoreStart } from '@kbn/core-lifecycle-browser';
import { type FieldListFiltersProps } from '../components/field_list_filters';
import { type FieldListItem, type FieldTypeKnown, GetCustomFieldType } from '../types';
import { getFieldIconType } from '../utils/field_types';
+import { fieldNameWildcardMatcher } from '../utils/field_name_wildcard_matcher';
const htmlId = htmlIdGenerator('fieldList');
@@ -74,11 +75,7 @@ export function useFieldFilters({
onFilterField:
fieldSearchHighlight?.length || selectedFieldTypes.length > 0
? (field: T) => {
- if (
- fieldSearchHighlight?.length &&
- !field.name?.toLowerCase().includes(fieldSearchHighlight) &&
- !field.displayName?.toLowerCase().includes(fieldSearchHighlight)
- ) {
+ if (fieldSearchHighlight && !fieldNameWildcardMatcher(field, fieldSearchHighlight)) {
return false;
}
if (selectedFieldTypes.length > 0) {
diff --git a/src/plugins/unified_field_list/public/utils/field_name_wildcard_matcher.test.tsx b/src/plugins/unified_field_list/public/utils/field_name_wildcard_matcher.test.tsx
new file mode 100644
index 0000000000000..2637ddf4046d2
--- /dev/null
+++ b/src/plugins/unified_field_list/public/utils/field_name_wildcard_matcher.test.tsx
@@ -0,0 +1,38 @@
+/*
+ * 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';
+import { fieldNameWildcardMatcher } from './field_name_wildcard_matcher';
+
+describe('UnifiedFieldList fieldNameWildcardMatcher()', () => {
+ it('should work correctly', async () => {
+ expect(fieldNameWildcardMatcher({ displayName: 'test' } as DataViewField, 'no')).toBe(false);
+ expect(
+ fieldNameWildcardMatcher({ displayName: 'test', name: 'yes' } as DataViewField, 'yes')
+ ).toBe(true);
+
+ const search = 'test*ue';
+ expect(fieldNameWildcardMatcher({ displayName: 'test' } as DataViewField, search)).toBe(false);
+ expect(fieldNameWildcardMatcher({ displayName: 'test.value' } as DataViewField, search)).toBe(
+ true
+ );
+ expect(fieldNameWildcardMatcher({ name: 'test.this_value' } as DataViewField, search)).toBe(
+ true
+ );
+ expect(fieldNameWildcardMatcher({ name: 'message.test' } as DataViewField, search)).toBe(false);
+ expect(
+ fieldNameWildcardMatcher({ name: 'test.this_value.maybe' } as DataViewField, search)
+ ).toBe(false);
+ expect(
+ fieldNameWildcardMatcher({ name: 'test.this_value.maybe' } as DataViewField, `${search}*`)
+ ).toBe(true);
+ expect(
+ fieldNameWildcardMatcher({ name: 'test.this_value.maybe' } as DataViewField, '*value*')
+ ).toBe(true);
+ });
+});
diff --git a/src/plugins/unified_field_list/public/utils/field_name_wildcard_matcher.ts b/src/plugins/unified_field_list/public/utils/field_name_wildcard_matcher.ts
new file mode 100644
index 0000000000000..98b0e64b7bc78
--- /dev/null
+++ b/src/plugins/unified_field_list/public/utils/field_name_wildcard_matcher.ts
@@ -0,0 +1,34 @@
+/*
+ * 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 { escapeRegExp, memoize } from 'lodash';
+
+const makeRegEx = memoize(function makeRegEx(glob: string) {
+ const globRegex = glob.split('*').map(escapeRegExp).join('.*');
+ return new RegExp(globRegex.includes('*') ? `^${globRegex}$` : globRegex, 'i');
+});
+
+/**
+ * Checks if field displayName or name matches the provided search string.
+ * The search string can have wildcard.
+ * @param field
+ * @param fieldSearchHighlight
+ */
+export const fieldNameWildcardMatcher = (
+ field: { name: string; displayName?: string },
+ fieldSearchHighlight: string
+): boolean => {
+ if (!fieldSearchHighlight) {
+ return false;
+ }
+
+ return (
+ (!!field.displayName && makeRegEx(fieldSearchHighlight).test(field.displayName)) ||
+ makeRegEx(fieldSearchHighlight).test(field.name)
+ );
+};