mirror of
https://github.com/elastic/kibana.git
synced 2025-06-28 11:05:39 -04:00
Aligns the Portable Dashboard renderer and the Control Group renderer to a new API structure using `useImperativeHandle` rather than the overcomplicated and mostly unused wrapper provider system.
165 lines
5.3 KiB
TypeScript
165 lines
5.3 KiB
TypeScript
/*
|
|
* 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 React, { useEffect, useState } from 'react';
|
|
import { v4 as uuidv4 } from 'uuid';
|
|
import { lastValueFrom } from 'rxjs';
|
|
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
|
|
import type { DataView } from '@kbn/data-views-plugin/public';
|
|
import type { NavigationPublicPluginStart } from '@kbn/navigation-plugin/public';
|
|
import type { Filter, Query, TimeRange } from '@kbn/es-query';
|
|
import { ViewMode } from '@kbn/embeddable-plugin/public';
|
|
import {
|
|
EuiCallOut,
|
|
EuiLoadingSpinner,
|
|
EuiPanel,
|
|
EuiSpacer,
|
|
EuiText,
|
|
EuiTitle,
|
|
} from '@elastic/eui';
|
|
import { AwaitingControlGroupAPI, ControlGroupRenderer } from '@kbn/controls-plugin/public';
|
|
import { PLUGIN_ID } from './constants';
|
|
|
|
interface Props {
|
|
data: DataPublicPluginStart;
|
|
dataView: DataView;
|
|
navigation: NavigationPublicPluginStart;
|
|
}
|
|
|
|
export const SearchExample = ({ data, dataView, navigation }: Props) => {
|
|
const [controlFilters, setControlFilters] = useState<Filter[]>([]);
|
|
const [controlGroupAPI, setControlGroupAPI] = useState<AwaitingControlGroupAPI>();
|
|
const [hits, setHits] = useState(0);
|
|
const [filters, setFilters] = useState<Filter[]>([]);
|
|
const [isSearching, setIsSearching] = useState(false);
|
|
const [query, setQuery] = useState<Query | undefined>({
|
|
language: 'kuery',
|
|
query: '',
|
|
});
|
|
const [timeRange, setTimeRange] = useState<TimeRange>({ from: 'now-7d', to: 'now' });
|
|
|
|
useEffect(() => {
|
|
if (!controlGroupAPI) {
|
|
return;
|
|
}
|
|
const subscription = controlGroupAPI.onFiltersPublished$.subscribe((newFilters) => {
|
|
setControlFilters([...newFilters]);
|
|
});
|
|
return () => {
|
|
subscription.unsubscribe();
|
|
};
|
|
}, [controlGroupAPI]);
|
|
|
|
useEffect(() => {
|
|
const abortController = new AbortController();
|
|
const search = async () => {
|
|
setIsSearching(true);
|
|
const searchSource = await data.search.searchSource.create();
|
|
searchSource.setField('index', dataView);
|
|
searchSource.setField('size', 0);
|
|
searchSource.setField('filter', [
|
|
...filters,
|
|
...controlFilters,
|
|
data.query.timefilter.timefilter.createFilter(dataView, timeRange),
|
|
] as Filter[]);
|
|
searchSource.setField('query', query);
|
|
const { rawResponse: resp } = await lastValueFrom(
|
|
searchSource.fetch$({
|
|
abortSignal: abortController.signal,
|
|
sessionId: uuidv4(),
|
|
legacyHitsTotal: false,
|
|
})
|
|
);
|
|
const total = resp.hits?.total as undefined | { relation: string; value: number };
|
|
if (total !== undefined) {
|
|
setHits(total.value);
|
|
}
|
|
setIsSearching(false);
|
|
};
|
|
|
|
search().catch((error) => {
|
|
setIsSearching(false);
|
|
if (error.name === 'AbortError') {
|
|
// ignore abort errors
|
|
} else {
|
|
// eslint-disable-next-line no-console
|
|
console.error(error);
|
|
}
|
|
});
|
|
|
|
return () => {
|
|
abortController.abort();
|
|
};
|
|
}, [controlFilters, data, dataView, filters, query, timeRange]);
|
|
|
|
return (
|
|
<>
|
|
<EuiTitle>
|
|
<h2>Search example</h2>
|
|
</EuiTitle>
|
|
<EuiText>
|
|
<p>
|
|
Pass filters, query, and time range to narrow controls. Combine search bar filters with
|
|
controls filters to narrow results.
|
|
</p>
|
|
</EuiText>
|
|
<EuiSpacer size="m" />
|
|
<EuiPanel hasBorder={true}>
|
|
<navigation.ui.TopNavMenu
|
|
appName={PLUGIN_ID}
|
|
dateRangeFrom={timeRange.from}
|
|
dateRangeTo={timeRange.to}
|
|
filters={filters}
|
|
indexPatterns={[dataView]}
|
|
onFiltersUpdated={(newFilters) => {
|
|
// filterManager.setFilters populates filter.meta so filter pill has pretty title
|
|
data.query.filterManager.setFilters(newFilters);
|
|
setFilters(newFilters);
|
|
}}
|
|
onQuerySubmit={({ dateRange, query: newQuery }) => {
|
|
setQuery(newQuery);
|
|
setTimeRange(dateRange);
|
|
}}
|
|
query={query}
|
|
showSearchBar={true}
|
|
/>
|
|
<ControlGroupRenderer
|
|
filters={filters}
|
|
getCreationOptions={async (initialInput, builder) => {
|
|
await builder.addDataControlFromField(initialInput, {
|
|
dataViewId: dataView.id!,
|
|
title: 'Destintion country',
|
|
fieldName: 'geo.dest',
|
|
width: 'medium',
|
|
grow: false,
|
|
});
|
|
await builder.addDataControlFromField(initialInput, {
|
|
dataViewId: dataView.id!,
|
|
fieldName: 'bytes',
|
|
width: 'medium',
|
|
grow: true,
|
|
title: 'Bytes',
|
|
});
|
|
return {
|
|
initialInput: {
|
|
...initialInput,
|
|
viewMode: ViewMode.VIEW,
|
|
},
|
|
};
|
|
}}
|
|
query={query}
|
|
ref={setControlGroupAPI}
|
|
timeRange={timeRange}
|
|
/>
|
|
<EuiCallOut title="Search results">
|
|
{isSearching ? <EuiLoadingSpinner size="l" /> : <p>Hits: {hits}</p>}
|
|
</EuiCallOut>
|
|
</EuiPanel>
|
|
</>
|
|
);
|
|
};
|