diff --git a/packages/kbn-unified-field-list/src/components/field_popover/field_popover.tsx b/packages/kbn-unified-field-list/src/components/field_popover/field_popover.tsx index 0b065cc504677..efa4d8d4c9e51 100644 --- a/packages/kbn-unified-field-list/src/components/field_popover/field_popover.tsx +++ b/packages/kbn-unified-field-list/src/components/field_popover/field_popover.tsx @@ -8,12 +8,20 @@ */ import React from 'react'; -import { EuiPopover, EuiPopoverProps, EuiPopoverTitle } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiPopover, + EuiPopoverProps, + EuiPopoverTitle, +} from '@elastic/eui'; import './field_popover.scss'; +import { euiThemeVars } from '@kbn/ui-theme'; export interface FieldPopoverProps extends EuiPopoverProps { renderHeader?: () => React.ReactNode; renderContent?: () => React.ReactNode; + renderFooter?: () => React.ReactNode; } export const FieldPopover: React.FC = ({ @@ -21,10 +29,12 @@ export const FieldPopover: React.FC = ({ closePopover, renderHeader, renderContent, + renderFooter, ...otherPopoverProps }) => { - let header = null; - let content = null; + let header: React.ReactNode | null = null; + let content: React.ReactNode | null = null; + let footer: React.ReactNode | null = null; if (isOpen) { try { @@ -40,6 +50,13 @@ export const FieldPopover: React.FC = ({ // eslint-disable-next-line no-console console.error(error); } + + try { + footer = renderFooter?.() || null; + } catch (error) { + // eslint-disable-next-line no-console + console.error(error); + } } return ( @@ -54,10 +71,27 @@ export const FieldPopover: React.FC = ({ {...otherPopoverProps} > {isOpen && ( - <> - {content && header ? {header} : header} - {content} - + + {Boolean(header) && ( + + {content ? {header} : header} + + )} + {content ? ( + + {content} + + ) : ( + content + )} + {Boolean(footer) && {footer}} + )} ); 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 658bf96dc76c9..7864976c1180f 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 @@ -309,22 +309,39 @@ function UnifiedFieldListItemComponent({ /> )} - - {searchMode === 'documents' && !!services.uiActions && ( - - )} ); }; + const renderFooter = useMemo(() => { + const uiActions = services.uiActions; + + if (searchMode !== 'documents' || !uiActions) { + return; + } + + return () => ( + + ); + }, [ + dataView, + field, + rawMultiFields, + searchMode, + services.uiActions, + stateService.creationOptions.originatingApp, + trackUiMetric, + workspaceSelectedFieldNames, + ]); + const value = useMemo( () => ({ id: field.name, @@ -393,6 +410,7 @@ function UnifiedFieldListItemComponent({ ? renderPopover : undefined } + renderFooter={renderFooter} /> ); } diff --git a/packages/kbn-unified-field-list/tsconfig.json b/packages/kbn-unified-field-list/tsconfig.json index f5015d66f0838..830e56ac6ab00 100644 --- a/packages/kbn-unified-field-list/tsconfig.json +++ b/packages/kbn-unified-field-list/tsconfig.json @@ -34,7 +34,8 @@ "@kbn/visualization-utils", "@kbn/esql-utils", "@kbn/search-types", - "@kbn/fields-metadata-plugin" + "@kbn/fields-metadata-plugin", + "@kbn/ui-theme" ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/lens/public/datasources/common/field_item.tsx b/x-pack/plugins/lens/public/datasources/common/field_item.tsx index efa13eb708e5a..b76dc9f4b9641 100644 --- a/x-pack/plugins/lens/public/datasources/common/field_item.tsx +++ b/x-pack/plugins/lens/public/datasources/common/field_item.tsx @@ -74,6 +74,8 @@ export type FieldItemProps = FieldItemIndexPatternFieldProps | FieldItemDatatabl export function InnerFieldItem(props: FieldItemProps) { const { + query, + filters, field, indexPattern, highlight, @@ -193,6 +195,56 @@ export function InnerFieldItem(props: FieldItemProps) { onAddFieldToWorkspace, }; + const renderFooter = useMemo(() => { + if (hideDetails || !indexPattern) { + return; + } + + if (dataViewField.type === 'geo_point' || dataViewField.type === 'geo_shape') { + return () => ( + indexPattern.spec } as unknown as DataView} + originatingApp={APP_ID} + uiActions={services.uiActions} + buttonProps={{ + 'data-test-subj': `lensVisualize-GeoField-${dataViewField.name}`, + }} + /> + ); + } + + return function ExplorerInDiscover() { + const exploreInDiscover = useMemo( + () => + getExploreInDiscover({ + query, + filters, + indexPattern, + dataViewField, + services, + }), + [] + ); + + return exploreInDiscover ? ( + + + {i18n.translate('xpack.lens.indexPattern.fieldExploreInDiscover', { + defaultMessage: 'Explore in Discover', + })} + + + ) : null; + }; + }, [dataViewField, filters, hideDetails, indexPattern, query, services]); + return (
  • ); @@ -264,108 +317,86 @@ function FieldItemPopoverContents( const { query, filters, indexPattern, dataViewField, dateRange, onAddFilter } = props; const services = useKibana().services; - const exploreInDiscover = useMemo(() => { - if (!indexPattern) { - return null; - } - const meta = { - id: indexPattern.id, - columns: [dataViewField.name], - filters: { - enabled: { - lucene: [], - kuery: [], - }, - disabled: { - lucene: [], - kuery: [], - }, - }, - }; - const { filters: newFilters, query: newQuery } = combineQueryAndFilters( - query, - filters, - meta, - [indexPattern], - getEsQueryConfig(services.uiSettings) - ); - const discoverLocator = services.share?.url.locators.get('DISCOVER_APP_LOCATOR'); - if (!discoverLocator || !services.application.capabilities.discover.show) { - return; - } - return discoverLocator.getRedirectUrl({ - dataViewSpec: indexPattern?.spec, - timeRange: services.data.query.timefilter.timefilter.getTime(), - filters: newFilters, - query: newQuery, - columns: meta.columns, - }); - }, [dataViewField.name, filters, indexPattern, query, services]); - if (!indexPattern) { return null; } return ( - <> - { - if (params.reason === 'no-data') { - // TODO: should we replace this with a default message "Analysis is not available for this field?" - return ( - - {i18n.translate('xpack.lens.indexPattern.fieldStatsNoData', { - defaultMessage: - 'Lens is unable to create visualizations with this field because it does not contain data. To create a visualization, drag and drop a different field.', - })} - - ); - } - if (params.reason === 'unsupported') { - return ( - - {params.element} - - ); - } - return params.element; - }} - /> + { + if (params.reason === 'no-data') { + // TODO: should we replace this with a default message "Analysis is not available for this field?" + return ( + + {i18n.translate('xpack.lens.indexPattern.fieldStatsNoData', { + defaultMessage: + 'Lens is unable to create visualizations with this field because it does not contain data. To create a visualization, drag and drop a different field.', + })} + + ); + } + if (params.reason === 'unsupported') { + return ( + {params.element} + ); + } + return params.element; + }} + /> + ); +} - {dataViewField.type === 'geo_point' || dataViewField.type === 'geo_shape' ? ( - indexPattern.spec } as unknown as DataView} - originatingApp={APP_ID} - uiActions={services.uiActions} - buttonProps={{ - 'data-test-subj': `lensVisualize-GeoField-${dataViewField.name}`, - }} - /> - ) : exploreInDiscover ? ( - - - {i18n.translate('xpack.lens.indexPattern.fieldExploreInDiscover', { - defaultMessage: 'Explore in Discover', - })} - - - ) : null} - +function getExploreInDiscover({ + query, + filters, + indexPattern, + dataViewField, + services, +}: Pick & { + filters: NonNullable; + indexPattern: NonNullable; + dataViewField: DataViewField; + services: LensAppServices; +}) { + const meta = { + id: indexPattern.id, + columns: [dataViewField.name], + filters: { + enabled: { + lucene: [], + kuery: [], + }, + disabled: { + lucene: [], + kuery: [], + }, + }, + }; + const { filters: newFilters, query: newQuery } = combineQueryAndFilters( + query, + filters, + meta, + [indexPattern], + getEsQueryConfig(services.uiSettings) ); + const discoverLocator = services.share?.url.locators.get('DISCOVER_APP_LOCATOR'); + if (!discoverLocator || !services.application.capabilities.discover.show) { + return; + } + return discoverLocator.getRedirectUrl({ + dataViewSpec: indexPattern?.spec, + timeRange: services.data.query.timefilter.timefilter.getTime(), + filters: newFilters, + query: newQuery, + columns: meta.columns, + }); }