diff --git a/packages/kbn-esql-utils/index.ts b/packages/kbn-esql-utils/index.ts index ee47c0321e2e3..9519e84624cbe 100644 --- a/packages/kbn-esql-utils/index.ts +++ b/packages/kbn-esql-utils/index.ts @@ -31,6 +31,7 @@ export { getQueryColumnsFromESQLQuery, isESQLColumnSortable, isESQLColumnGroupable, + isESQLFieldGroupable, TextBasedLanguages, } from './src'; diff --git a/packages/kbn-esql-utils/src/index.ts b/packages/kbn-esql-utils/src/index.ts index cf530be20d7ae..6cf2dd031bf07 100644 --- a/packages/kbn-esql-utils/src/index.ts +++ b/packages/kbn-esql-utils/src/index.ts @@ -31,4 +31,8 @@ export { getStartEndParams, hasStartEndParams, } from './utils/run_query'; -export { isESQLColumnSortable, isESQLColumnGroupable } from './utils/esql_fields_utils'; +export { + isESQLColumnSortable, + isESQLColumnGroupable, + isESQLFieldGroupable, +} from './utils/esql_fields_utils'; diff --git a/packages/kbn-esql-utils/src/utils/esql_fields_utils.test.ts b/packages/kbn-esql-utils/src/utils/esql_fields_utils.test.ts index bfc3a4d6708f0..5bf9980d4ae78 100644 --- a/packages/kbn-esql-utils/src/utils/esql_fields_utils.test.ts +++ b/packages/kbn-esql-utils/src/utils/esql_fields_utils.test.ts @@ -7,7 +7,12 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ import type { DatatableColumn } from '@kbn/expressions-plugin/common'; -import { isESQLColumnSortable, isESQLColumnGroupable } from './esql_fields_utils'; +import { + isESQLColumnSortable, + isESQLColumnGroupable, + isESQLFieldGroupable, +} from './esql_fields_utils'; +import type { FieldSpec } from '@kbn/data-views-plugin/common'; describe('esql fields helpers', () => { describe('isESQLColumnSortable', () => { @@ -104,4 +109,46 @@ describe('esql fields helpers', () => { expect(isESQLColumnGroupable(keywordField)).toBeTruthy(); }); }); + + describe('isESQLFieldGroupable', () => { + it('returns false for unsupported fields', () => { + const fieldSpec: FieldSpec = { + name: 'unsupported', + type: 'unknown', + esTypes: ['unknown'], + searchable: true, + aggregatable: false, + isNull: false, + }; + + expect(isESQLFieldGroupable(fieldSpec)).toBeFalsy(); + }); + + it('returns false for counter fields', () => { + const fieldSpec: FieldSpec = { + name: 'tsbd_counter', + type: 'number', + esTypes: ['long'], + timeSeriesMetric: 'counter', + searchable: true, + aggregatable: false, + isNull: false, + }; + + expect(isESQLFieldGroupable(fieldSpec)).toBeFalsy(); + }); + + it('returns true for everything else', () => { + const fieldSpec: FieldSpec = { + name: 'sortable', + type: 'string', + esTypes: ['keyword'], + searchable: true, + aggregatable: false, + isNull: false, + }; + + expect(isESQLFieldGroupable(fieldSpec)).toBeTruthy(); + }); + }); }); diff --git a/packages/kbn-esql-utils/src/utils/esql_fields_utils.ts b/packages/kbn-esql-utils/src/utils/esql_fields_utils.ts index e2c5c785f8f58..fa0ae534197f6 100644 --- a/packages/kbn-esql-utils/src/utils/esql_fields_utils.ts +++ b/packages/kbn-esql-utils/src/utils/esql_fields_utils.ts @@ -7,6 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import type { FieldSpec } from '@kbn/data-views-plugin/common'; import type { DatatableColumn } from '@kbn/expressions-plugin/common'; const SPATIAL_FIELDS = ['geo_point', 'geo_shape', 'point', 'shape']; @@ -40,22 +41,30 @@ export const isESQLColumnSortable = (column: DatatableColumn): boolean => { return true; }; +// Helper function to check if a field is groupable based on its type and esType +const isGroupable = (type: string | undefined, esType: string | undefined): boolean => { + // we don't allow grouping on the unknown field types + if (type === UNKNOWN_FIELD) { + return false; + } + // we don't allow grouping on tsdb counter fields + if (esType && esType.indexOf(TSDB_COUNTER_FIELDS_PREFIX) !== -1) { + return false; + } + return true; +}; + /** * Check if a column is groupable (| STATS ... BY ). * * @param column The DatatableColumn of the field. * @returns True if the column is groupable, false otherwise. */ - export const isESQLColumnGroupable = (column: DatatableColumn): boolean => { - // we don't allow grouping on the unknown field types - if (column.meta?.type === UNKNOWN_FIELD) { - return false; - } - // we don't allow grouping on tsdb counter fields - if (column.meta?.esType && column.meta?.esType?.indexOf(TSDB_COUNTER_FIELDS_PREFIX) !== -1) { - return false; - } + return isGroupable(column.meta?.type, column.meta?.esType); +}; - return true; +export const isESQLFieldGroupable = (field: FieldSpec): boolean => { + if (field.timeSeriesMetric === 'counter') return false; + return isGroupable(field.type, field.esTypes?.[0]); }; diff --git a/packages/kbn-field-utils/index.ts b/packages/kbn-field-utils/index.ts index ae77f0d1524fd..d3ce774177dcc 100644 --- a/packages/kbn-field-utils/index.ts +++ b/packages/kbn-field-utils/index.ts @@ -25,6 +25,7 @@ export { comboBoxFieldOptionMatcher, getFieldSearchMatchingHighlight, } from './src/utils/field_name_wildcard_matcher'; +export { fieldSupportsBreakdown } from './src/utils/field_supports_breakdown'; export { FieldIcon, type FieldIconProps, getFieldIconProps } from './src/components/field_icon'; export { FieldDescription, type FieldDescriptionProps } from './src/components/field_description'; diff --git a/src/plugins/unified_histogram/public/utils/field_supports_breakdown.test.ts b/packages/kbn-field-utils/src/utils/field_supports_breakdown.test.ts similarity index 100% rename from src/plugins/unified_histogram/public/utils/field_supports_breakdown.test.ts rename to packages/kbn-field-utils/src/utils/field_supports_breakdown.test.ts diff --git a/src/plugins/unified_histogram/public/utils/field_supports_breakdown.ts b/packages/kbn-field-utils/src/utils/field_supports_breakdown.ts similarity index 65% rename from src/plugins/unified_histogram/public/utils/field_supports_breakdown.ts rename to packages/kbn-field-utils/src/utils/field_supports_breakdown.ts index 12fdfbf20aa3b..3177d40012b57 100644 --- a/src/plugins/unified_histogram/public/utils/field_supports_breakdown.ts +++ b/packages/kbn-field-utils/src/utils/field_supports_breakdown.ts @@ -7,12 +7,18 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { DataViewField } from '@kbn/data-views-plugin/public'; +import { type DataViewField } from '@kbn/data-views-plugin/common'; +import { KNOWN_FIELD_TYPES } from './field_types'; -const supportedTypes = new Set(['string', 'boolean', 'number', 'ip']); +const supportedTypes = new Set([ + KNOWN_FIELD_TYPES.STRING, + KNOWN_FIELD_TYPES.BOOLEAN, + KNOWN_FIELD_TYPES.NUMBER, + KNOWN_FIELD_TYPES.IP, +]); export const fieldSupportsBreakdown = (field: DataViewField) => - supportedTypes.has(field.type) && + supportedTypes.has(field.type as KNOWN_FIELD_TYPES) && field.aggregatable && !field.scripted && field.timeSeriesMetric !== 'counter'; diff --git a/packages/kbn-unified-field-list/src/components/field_popover/field_popover_header.test.tsx b/packages/kbn-unified-field-list/src/components/field_popover/field_popover_header.test.tsx index 19fee33bc6eaf..a4e322f2b681c 100644 --- a/packages/kbn-unified-field-list/src/components/field_popover/field_popover_header.test.tsx +++ b/packages/kbn-unified-field-list/src/components/field_popover/field_popover_header.test.tsx @@ -38,6 +38,7 @@ describe('UnifiedFieldList ', () => { field={field} closePopover={mockClose} onAddFieldToWorkspace={jest.fn()} + onAddBreakdownField={jest.fn()} onAddFilter={jest.fn()} onEditField={jest.fn()} onDeleteField={jest.fn()} @@ -45,6 +46,9 @@ describe('UnifiedFieldList ', () => { ); expect(wrapper.text()).toBe(fieldName); + expect( + wrapper.find(`[data-test-subj="fieldPopoverHeader_addBreakdownField-${fieldName}"]`).exists() + ).toBeTruthy(); expect( wrapper.find(`[data-test-subj="fieldPopoverHeader_addField-${fieldName}"]`).exists() ).toBeTruthy(); @@ -57,7 +61,29 @@ describe('UnifiedFieldList ', () => { expect( wrapper.find(`[data-test-subj="fieldPopoverHeader_deleteField-${fieldName}"]`).exists() ).toBeTruthy(); - expect(wrapper.find(EuiButtonIcon)).toHaveLength(4); + expect(wrapper.find(EuiButtonIcon)).toHaveLength(5); + }); + + it('should correctly handle add-breakdown-field action', async () => { + const mockClose = jest.fn(); + const mockAddBreakdownField = jest.fn(); + const fieldName = 'extension'; + const field = dataView.fields.find((f) => f.name === fieldName)!; + const wrapper = mountWithIntl( + + ); + + wrapper + .find(`[data-test-subj="fieldPopoverHeader_addBreakdownField-${fieldName}"]`) + .first() + .simulate('click'); + + expect(mockClose).toHaveBeenCalled(); + expect(mockAddBreakdownField).toHaveBeenCalledWith(field); }); it('should correctly handle add-field action', async () => { diff --git a/packages/kbn-unified-field-list/src/components/field_popover/field_popover_header.tsx b/packages/kbn-unified-field-list/src/components/field_popover/field_popover_header.tsx index 65d5a1da98034..3babd05871913 100644 --- a/packages/kbn-unified-field-list/src/components/field_popover/field_popover_header.tsx +++ b/packages/kbn-unified-field-list/src/components/field_popover/field_popover_header.tsx @@ -31,6 +31,7 @@ export interface FieldPopoverHeaderProps { buttonAddFilterProps?: Partial; buttonEditFieldProps?: Partial; buttonDeleteFieldProps?: Partial; + onAddBreakdownField?: (field: DataViewField | undefined) => void; onAddFieldToWorkspace?: (field: DataViewField) => unknown; onAddFilter?: AddFieldFilterHandler; onEditField?: (fieldName: string) => unknown; @@ -47,6 +48,7 @@ export const FieldPopoverHeader: React.FC = ({ buttonAddFilterProps, buttonEditFieldProps, buttonDeleteFieldProps, + onAddBreakdownField, onAddFieldToWorkspace, onAddFilter, onEditField, @@ -82,6 +84,13 @@ export const FieldPopoverHeader: React.FC = ({ defaultMessage: 'Delete data view field', }); + const addBreakdownFieldTooltip = i18n.translate( + 'unifiedFieldList.fieldPopover.addBreakdownFieldLabel', + { + defaultMessage: 'Add breakdown', + } + ); + return ( <> @@ -108,6 +117,21 @@ export const FieldPopoverHeader: React.FC = ({ )} + {onAddBreakdownField && ( + + + { + closePopover(); + onAddBreakdownField(field); + }} + /> + + + )} {onAddFilter && field.filterable && !field.scripted && ( diff --git a/packages/kbn-unified-field-list/src/containers/unified_field_list_item/field_list_item.test.tsx b/packages/kbn-unified-field-list/src/containers/unified_field_list_item/field_list_item.test.tsx index a5b1955aeca2e..671a4175ad10a 100644 --- a/packages/kbn-unified-field-list/src/containers/unified_field_list_item/field_list_item.test.tsx +++ b/packages/kbn-unified-field-list/src/containers/unified_field_list_item/field_list_item.test.tsx @@ -43,10 +43,12 @@ async function getComponent({ selected = false, field, canFilter = true, + isBreakdownSupported = true, }: { selected?: boolean; field?: DataViewField; canFilter?: boolean; + isBreakdownSupported?: boolean; }) { const finalField = field ?? @@ -76,6 +78,7 @@ async function getComponent({ dataView: stubDataView, field: finalField, ...(canFilter && { onAddFilter: jest.fn() }), + ...(isBreakdownSupported && { onAddBreakdownField: jest.fn() }), onAddFieldToWorkspace: jest.fn(), onRemoveFieldFromWorkspace: jest.fn(), onEditField: jest.fn(), @@ -137,6 +140,34 @@ describe('UnifiedFieldListItem', function () { expect(comp.find(FieldItemButton).prop('onClick')).toBeUndefined(); }); + + it('should not show addBreakdownField action button if not supported', async function () { + const field = new DataViewField({ + name: 'extension.keyword', + type: 'string', + esTypes: ['keyword'], + aggregatable: true, + searchable: true, + }); + const { comp } = await getComponent({ + field, + isBreakdownSupported: false, + }); + + await act(async () => { + const fieldItem = findTestSubject(comp, 'field-extension.keyword-showDetails'); + await fieldItem.simulate('click'); + await comp.update(); + }); + + await comp.update(); + + expect( + comp + .find('[data-test-subj="fieldPopoverHeader_addBreakdownField-extension.keyword"]') + .exists() + ).toBeFalsy(); + }); it('should request field stats', async function () { const field = new DataViewField({ name: 'machine.os.raw', @@ -189,6 +220,11 @@ describe('UnifiedFieldListItem', function () { await comp.update(); expect(comp.find(EuiPopover).prop('isOpen')).toBe(true); + expect( + comp + .find('[data-test-subj="fieldPopoverHeader_addBreakdownField-extension.keyword"]') + .exists() + ).toBeTruthy(); expect( comp.find('[data-test-subj="fieldPopoverHeader_addField-extension.keyword"]').exists() ).toBeTruthy(); diff --git a/packages/kbn-unified-field-list/src/containers/unified_field_list_item/field_list_item.tsx b/packages/kbn-unified-field-list/src/containers/unified_field_list_item/field_list_item.tsx index b139e7b5685c5..2ff7d22de39da 100644 --- a/packages/kbn-unified-field-list/src/containers/unified_field_list_item/field_list_item.tsx +++ b/packages/kbn-unified-field-list/src/containers/unified_field_list_item/field_list_item.tsx @@ -15,6 +15,8 @@ import type { FieldsMetadataPublicStart } from '@kbn/fields-metadata-plugin/publ import { Draggable } from '@kbn/dom-drag-drop'; import type { DataView, DataViewField } from '@kbn/data-views-plugin/public'; import { Filter } from '@kbn/es-query'; +import { fieldSupportsBreakdown } from '@kbn/field-utils'; +import { isESQLFieldGroupable } from '@kbn/esql-utils'; import type { SearchMode } from '../../types'; import { FieldItemButton, type FieldItemButtonProps } from '../../components/field_item_button'; import { @@ -140,6 +142,10 @@ export interface UnifiedFieldListItemProps { * The currently selected data view */ dataView: DataView; + /** + * Callback to update breakdown field + */ + onAddBreakdownField?: (breakdownField: DataViewField | undefined) => void; /** * Callback to add/select the field */ @@ -215,6 +221,7 @@ function UnifiedFieldListItemComponent({ field, highlight, dataView, + onAddBreakdownField, onAddFieldToWorkspace, onRemoveFieldFromWorkspace, onAddFilter, @@ -232,6 +239,9 @@ function UnifiedFieldListItemComponent({ }: UnifiedFieldListItemProps) { const [infoIsOpen, setOpen] = useState(false); + const isBreakdownSupported = + searchMode === 'documents' ? fieldSupportsBreakdown(field) : isESQLFieldGroupable(field); + const addFilterAndClosePopover: typeof onAddFilter | undefined = useMemo( () => onAddFilter @@ -394,13 +404,14 @@ function UnifiedFieldListItemComponent({ data-test-subj={stateService.creationOptions.dataTestSubj?.fieldListItemPopoverDataTestSubj} renderHeader={() => ( )} diff --git a/packages/kbn-unified-field-list/src/containers/unified_field_list_sidebar/field_list_sidebar.tsx b/packages/kbn-unified-field-list/src/containers/unified_field_list_sidebar/field_list_sidebar.tsx index a7d8fb4616cb0..2d23903c34ea3 100644 --- a/packages/kbn-unified-field-list/src/containers/unified_field_list_sidebar/field_list_sidebar.tsx +++ b/packages/kbn-unified-field-list/src/containers/unified_field_list_sidebar/field_list_sidebar.tsx @@ -48,6 +48,7 @@ export type UnifiedFieldListSidebarCustomizableProps = Pick< | 'dataView' | 'trackUiMetric' | 'onAddFilter' + | 'onAddBreakdownField' | 'onAddFieldToWorkspace' | 'onRemoveFieldFromWorkspace' | 'additionalFilters' @@ -161,6 +162,7 @@ export const UnifiedFieldListSidebarComponent: React.FC (
  • ), @@ -298,6 +301,7 @@ export const UnifiedFieldListSidebarComponent: React.FC + isOfAggregateQueryType(query) + ? dataView?.isTimeBased() && !hasTransformationalCommand(query.esql) + : true, + [dataView, query] + ); + + const onAddBreakdownField = useCallback( + (field: DataViewField | undefined) => { + stateContainer.appState.update({ breakdownField: field?.name }); + }, + [stateContainer] + ); + const onFieldEdited = useCallback( async ({ removedFieldName }: { removedFieldName?: string } = {}) => { if (removedFieldName && currentColumns.includes(removedFieldName)) { @@ -423,18 +438,19 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) { sidebarToggleState$={sidebarToggleState$} sidebarPanel={ } mainPanel={ diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.test.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.test.tsx index 6147bb916dad4..296d403da6901 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.test.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.test.tsx @@ -162,6 +162,7 @@ function getCompProps(options?: { hits?: DataTableRecord[] }): DiscoverSidebarRe result: hits, }) as DataDocuments$, onChangeDataView: jest.fn(), + onAddBreakdownField: jest.fn(), onAddFilter: jest.fn(), onAddField: jest.fn(), onRemoveField: jest.fn(), @@ -397,6 +398,19 @@ describe('discover responsive sidebar', function () { expect(ExistingFieldsServiceApi.loadFieldExisting).not.toHaveBeenCalled(); }); + it('should allow adding breakdown field', async function () { + const comp = await mountComponent(props); + const availableFields = findTestSubject(comp, 'fieldListGroupedAvailableFields'); + await act(async () => { + const button = findTestSubject(availableFields, 'field-extension-showDetails'); + button.simulate('click'); + comp.update(); + }); + + comp.update(); + findTestSubject(comp, 'fieldPopoverHeader_addBreakdownField-extension').simulate('click'); + expect(props.onAddBreakdownField).toHaveBeenCalled(); + }); it('should allow selecting fields', async function () { const comp = await mountComponent(props); const availableFields = findTestSubject(comp, 'fieldListGroupedAvailableFields'); diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx index 80a3b9d412c76..17a3e9def8b23 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx @@ -87,6 +87,10 @@ export interface DiscoverSidebarResponsiveProps { * hits fetched from ES, displayed in the doc table */ documents$: DataDocuments$; + /** + * Callback to update breakdown field + */ + onAddBreakdownField?: (breakdownField: DataViewField | undefined) => void; /** * Callback function when selecting a field */ @@ -151,6 +155,7 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) selectedDataView, columns, trackUiMetric, + onAddBreakdownField, onAddFilter, onFieldEdited, onDataViewCreated, @@ -373,23 +378,24 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) {selectedDataView ? ( ) : null} diff --git a/src/plugins/unified_histogram/public/chart/breakdown_field_selector.tsx b/src/plugins/unified_histogram/public/chart/breakdown_field_selector.tsx index 0d6aa1e75fe80..418892bd5434f 100644 --- a/src/plugins/unified_histogram/public/chart/breakdown_field_selector.tsx +++ b/src/plugins/unified_histogram/public/chart/breakdown_field_selector.tsx @@ -9,7 +9,12 @@ import React, { useCallback, useMemo } from 'react'; import { EuiSelectableOption } from '@elastic/eui'; -import { FieldIcon, getFieldIconProps, comboBoxFieldOptionMatcher } from '@kbn/field-utils'; +import { + FieldIcon, + getFieldIconProps, + comboBoxFieldOptionMatcher, + fieldSupportsBreakdown, +} from '@kbn/field-utils'; import { css } from '@emotion/react'; import { isESQLColumnGroupable } from '@kbn/esql-utils'; import { type DataView, DataViewField } from '@kbn/data-views-plugin/common'; @@ -17,7 +22,6 @@ import type { DatatableColumn } from '@kbn/expressions-plugin/common'; import { convertDatatableColumnToDataViewFieldSpec } from '@kbn/data-view-utils'; import { i18n } from '@kbn/i18n'; import { UnifiedHistogramBreakdownContext } from '../types'; -import { fieldSupportsBreakdown } from '../utils/field_supports_breakdown'; import { ToolbarSelector, ToolbarSelectorProps, diff --git a/src/plugins/unified_histogram/public/index.ts b/src/plugins/unified_histogram/public/index.ts index fda59c78819da..f79db6ce8a51a 100644 --- a/src/plugins/unified_histogram/public/index.ts +++ b/src/plugins/unified_histogram/public/index.ts @@ -36,6 +36,5 @@ export type { } from './types'; export { UnifiedHistogramFetchStatus, UnifiedHistogramExternalVisContextStatus } from './types'; export { canImportVisContext } from './utils/external_vis_context'; -export { fieldSupportsBreakdown } from './utils/field_supports_breakdown'; export const plugin = () => new UnifiedHistogramPublicPlugin(); diff --git a/src/plugins/unified_histogram/public/services/lens_vis_service.ts b/src/plugins/unified_histogram/public/services/lens_vis_service.ts index 2367e729b5a70..04bf810848f29 100644 --- a/src/plugins/unified_histogram/public/services/lens_vis_service.ts +++ b/src/plugins/unified_histogram/public/services/lens_vis_service.ts @@ -36,6 +36,7 @@ import { LegendSize } from '@kbn/visualizations-plugin/public'; import { XYConfiguration } from '@kbn/visualizations-plugin/common'; import type { Datatable, DatatableColumn } from '@kbn/expressions-plugin/common'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import { fieldSupportsBreakdown } from '@kbn/field-utils'; import { UnifiedHistogramExternalVisContextStatus, UnifiedHistogramSuggestionContext, @@ -49,7 +50,6 @@ import { injectESQLQueryIntoLensLayers, } from '../utils/external_vis_context'; import { computeInterval } from '../utils/compute_interval'; -import { fieldSupportsBreakdown } from '../utils/field_supports_breakdown'; import { shouldDisplayHistogram } from '../layout/helpers'; import { enrichLensAttributesWithTablesData } from '../utils/lens_vis_from_table'; diff --git a/test/functional/apps/discover/esql/_esql_view.ts b/test/functional/apps/discover/esql/_esql_view.ts index 272de320d2051..18841d07dac3d 100644 --- a/test/functional/apps/discover/esql/_esql_view.ts +++ b/test/functional/apps/discover/esql/_esql_view.ts @@ -843,6 +843,23 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const list = await discover.getHistogramLegendList(); expect(list).to.eql(['css', 'gif', 'jpg', 'php', 'png']); }); + + it('should choose breakdown field when selected from field stats', async () => { + await discover.selectTextBaseLang(); + await header.waitUntilLoadingHasFinished(); + await discover.waitUntilSearchingHasFinished(); + + const testQuery = 'from logstash-*'; + await monacoEditor.setCodeEditorValue(testQuery); + await testSubjects.click('querySubmitButton'); + await header.waitUntilLoadingHasFinished(); + await discover.waitUntilSearchingHasFinished(); + + await unifiedFieldList.clickFieldListAddBreakdownField('extension'); + await header.waitUntilLoadingHasFinished(); + const list = await discover.getHistogramLegendList(); + expect(list).to.eql(['css', 'gif', 'jpg', 'php', 'png']); + }); }); }); } diff --git a/test/functional/apps/discover/group1/_discover_histogram_breakdown.ts b/test/functional/apps/discover/group1/_discover_histogram_breakdown.ts index 5ce8732e5355a..6f04a6df1b6b4 100644 --- a/test/functional/apps/discover/group1/_discover_histogram_breakdown.ts +++ b/test/functional/apps/discover/group1/_discover_histogram_breakdown.ts @@ -14,11 +14,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const filterBar = getService('filterBar'); - const { common, discover, header, timePicker } = getPageObjects([ + const { common, discover, header, timePicker, unifiedFieldList } = getPageObjects([ 'common', 'discover', 'header', 'timePicker', + 'unifiedFieldList', ]); describe('discover unified histogram breakdown', function describeIndexTests() { @@ -36,6 +37,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover'); }); + it('should apply breakdown when selected from field stats', async () => { + await unifiedFieldList.clickFieldListAddBreakdownField('geo.dest'); + await header.waitUntilLoadingHasFinished(); + const list = await discover.getHistogramLegendList(); + expect(list).to.eql(['CN', 'IN', 'US', 'Other']); + }); + it('should choose breakdown field', async () => { await discover.chooseBreakdownField('extension.raw'); await header.waitUntilLoadingHasFinished(); diff --git a/test/functional/page_objects/unified_field_list.ts b/test/functional/page_objects/unified_field_list.ts index 14006c8c5d62e..4751769f717bd 100644 --- a/test/functional/page_objects/unified_field_list.ts +++ b/test/functional/page_objects/unified_field_list.ts @@ -226,6 +226,16 @@ export class UnifiedFieldListPageObject extends FtrService { await this.testSubjects.missingOrFail(`fieldVisualize-${field}`); } + public async clickFieldListAddBreakdownField(field: string) { + const addBreakdownFieldTestSubj = `fieldPopoverHeader_addBreakdownField-${field}`; + if (!(await this.testSubjects.exists(addBreakdownFieldTestSubj))) { + // field has to be open + await this.clickFieldListItem(field); + } + await this.testSubjects.click(addBreakdownFieldTestSubj); + await this.header.waitUntilLoadingHasFinished(); + } + public async clickFieldListPlusFilter(field: string, value: string) { const plusFilterTestSubj = `plus-${field}-${value}`; if (!(await this.testSubjects.exists(plusFilterTestSubj))) { diff --git a/x-pack/plugins/observability_solution/dataset_quality/public/hooks/use_degraded_docs_chart.ts b/x-pack/plugins/observability_solution/dataset_quality/public/hooks/use_degraded_docs_chart.ts index d3fad141335de..90779613f0c06 100644 --- a/x-pack/plugins/observability_solution/dataset_quality/public/hooks/use_degraded_docs_chart.ts +++ b/x-pack/plugins/observability_solution/dataset_quality/public/hooks/use_degraded_docs_chart.ts @@ -7,10 +7,10 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import type { Action } from '@kbn/ui-actions-plugin/public'; -import { fieldSupportsBreakdown } from '@kbn/unified-histogram-plugin/public'; import { i18n } from '@kbn/i18n'; import { useEuiTheme } from '@elastic/eui'; import type { DataView, DataViewField } from '@kbn/data-views-plugin/common'; +import { fieldSupportsBreakdown } from '@kbn/field-utils'; import { DEFAULT_LOGS_DATA_VIEW } from '../../common/constants'; import { useCreateDataView } from './use_create_dataview'; import { useKibanaContextForPlugin } from '../utils'; diff --git a/x-pack/plugins/observability_solution/dataset_quality/tsconfig.json b/x-pack/plugins/observability_solution/dataset_quality/tsconfig.json index f0d82fadb54ad..8576b08bd9479 100644 --- a/x-pack/plugins/observability_solution/dataset_quality/tsconfig.json +++ b/x-pack/plugins/observability_solution/dataset_quality/tsconfig.json @@ -60,7 +60,8 @@ "@kbn/usage-collection-plugin", "@kbn/rison", "@kbn/task-manager-plugin", - "@kbn/core-application-browser" + "@kbn/core-application-browser", + "@kbn/field-utils" ], "exclude": [ "target/**/*"