Skip to content

Commit

Permalink
Merge branch 'main' into data_visualizer_map
Browse files Browse the repository at this point in the history
  • Loading branch information
kibanamachine authored Apr 29, 2024
2 parents a526688 + 957ff08 commit ef81e02
Show file tree
Hide file tree
Showing 91 changed files with 1,781 additions and 766 deletions.
2 changes: 2 additions & 0 deletions config/serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ xpack.index_management.enableIndexStats: false
xpack.index_management.editableIndexSettings: limited
# Disable Storage size column in the Data streams table from Index Management UI
xpack.index_management.enableDataStreamsStorageColumn: false
# Disable toggle for enabling data retention in DSL form from Index Management UI
xpack.index_management.enableTogglingDataRetention: false

# Keep deeplinks visible so that they are shown in the sidenav
dev_tools.deeplinks.navLinkStatus: visible
Expand Down
6 changes: 3 additions & 3 deletions examples/embeddable_examples/public/app/render_examples.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { TimeRange } from '@kbn/es-query';
import { useBatchedOptionalPublishingSubjects } from '@kbn/presentation-publishing';
import { SearchEmbeddableRenderer } from '../react_embeddables/search/search_embeddable_renderer';
import { SEARCH_EMBEDDABLE_ID } from '../react_embeddables/search/constants';
import type { Api, State } from '../react_embeddables/search/types';
import type { SearchApi, SearchSerializedState } from '../react_embeddables/search/types';

export const RenderExamples = () => {
const initialState = useMemo(() => {
Expand All @@ -48,7 +48,7 @@ export const RenderExamples = () => {
// only run onMount
}, []);

const [api, setApi] = useState<Api | null>(null);
const [api, setApi] = useState<SearchApi | null>(null);
const [hidePanelChrome, setHidePanelChrome] = useState<boolean>(false);
const [dataLoading, timeRange] = useBatchedOptionalPublishingSubjects(
api?.dataLoading,
Expand Down Expand Up @@ -104,7 +104,7 @@ export const RenderExamples = () => {

<EuiSpacer size="s" />

<ReactEmbeddableRenderer<State, Api>
<ReactEmbeddableRenderer<SearchSerializedState, SearchApi>
key={hidePanelChrome ? 'hideChrome' : 'showChrome'}
type={SEARCH_EMBEDDABLE_ID}
state={initialState}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n';
import { apiIsPresentationContainer } from '@kbn/presentation-containers';
import { EmbeddableApiContext } from '@kbn/presentation-publishing';
import { IncompatibleActionError, UiActionsStart } from '@kbn/ui-actions-plugin/public';
import { addPanelGrouping } from '../add_panel_grouping';
import { embeddableExamplesGrouping } from '../embeddable_examples_grouping';
import { ADD_DATA_TABLE_ACTION_ID, DATA_TABLE_ID } from './constants';

// -----------------------------------------------------------------------------
Expand All @@ -20,7 +20,7 @@ import { ADD_DATA_TABLE_ACTION_ID, DATA_TABLE_ID } from './constants';
export const registerCreateDataTableAction = (uiActions: UiActionsStart) => {
uiActions.registerAction<EmbeddableApiContext>({
id: ADD_DATA_TABLE_ACTION_ID,
grouping: [addPanelGrouping],
grouping: [embeddableExamplesGrouping],
getIconType: () => 'tableDensityNormal',
isCompatible: async ({ embeddable }) => {
return apiIsPresentationContainer(embeddable);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
* Side Public License, v 1.
*/

export const addPanelGrouping = {
export const embeddableExamplesGrouping = {
id: 'embeddableExamples',
getIconType: () => 'documentation',
getDisplayName: () => 'Embeddable examples',
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import { i18n } from '@kbn/i18n';
import { apiCanAddNewPanel } from '@kbn/presentation-containers';
import { EmbeddableApiContext } from '@kbn/presentation-publishing';
import { IncompatibleActionError, UiActionsStart } from '@kbn/ui-actions-plugin/public';
import { addPanelGrouping } from '../add_panel_grouping';
import { embeddableExamplesGrouping } from '../embeddable_examples_grouping';
import { ADD_EUI_MARKDOWN_ACTION_ID, EUI_MARKDOWN_ID } from './constants';
import { MarkdownEditorSerializedState } from './types';

// -----------------------------------------------------------------------------
// Create and register an action which allows this embeddable to be created from
Expand All @@ -20,14 +21,14 @@ import { ADD_EUI_MARKDOWN_ACTION_ID, EUI_MARKDOWN_ID } from './constants';
export const registerCreateEuiMarkdownAction = (uiActions: UiActionsStart) => {
uiActions.registerAction<EmbeddableApiContext>({
id: ADD_EUI_MARKDOWN_ACTION_ID,
grouping: [addPanelGrouping],
grouping: [embeddableExamplesGrouping],
getIconType: () => 'editorCodeBlock',
isCompatible: async ({ embeddable }) => {
return apiCanAddNewPanel(embeddable);
},
execute: async ({ embeddable }) => {
if (!apiCanAddNewPanel(embeddable)) throw new IncompatibleActionError();
embeddable.addNewPanel(
embeddable.addNewPanel<MarkdownEditorSerializedState>(
{
panelType: EUI_MARKDOWN_ID,
initialState: { content: '# hello world!' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,7 @@ export const markdownEmbeddableFactory: ReactEmbeddableFactory<
MarkdownEditorApi
> = {
type: EUI_MARKDOWN_ID,
deserializeState: (state) => {
/**
* Here we can run clientside migrations and inject references.
*/
return state.rawState as MarkdownEditorSerializedState;
},
deserializeState: (state) => state.rawState,
/**
* The buildEmbeddable function is async so you can async import the component or load a saved
* object here. The loading will be handed gracefully by the Presentation Container.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,21 @@ import { apiCanAddNewPanel } from '@kbn/presentation-containers';
import { EmbeddableApiContext } from '@kbn/presentation-publishing';
import { IncompatibleActionError } from '@kbn/ui-actions-plugin/public';
import { UiActionsPublicStart } from '@kbn/ui-actions-plugin/public/plugin';
import { embeddableExamplesGrouping } from '../embeddable_examples_grouping';
import { ADD_FIELD_LIST_ACTION_ID, FIELD_LIST_ID } from './constants';
import { addPanelGrouping } from '../add_panel_grouping';
import { FieldListSerializedStateState } from './types';

export const registerCreateFieldListAction = (uiActions: UiActionsPublicStart) => {
uiActions.registerAction<EmbeddableApiContext>({
id: ADD_FIELD_LIST_ACTION_ID,
grouping: [addPanelGrouping],
grouping: [embeddableExamplesGrouping],
getIconType: () => 'indexOpen',
isCompatible: async ({ embeddable }) => {
return apiCanAddNewPanel(embeddable);
},
execute: async ({ embeddable }) => {
if (!apiCanAddNewPanel(embeddable)) throw new IncompatibleActionError();
embeddable.addNewPanel({
embeddable.addNewPanel<FieldListSerializedStateState>({
panelType: FIELD_LIST_ID,
});
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export const getFieldListFactory = (
> = {
type: FIELD_LIST_ID,
deserializeState: (state) => {
const serializedState = cloneDeep(state.rawState) as FieldListSerializedStateState;
const serializedState = cloneDeep(state.rawState);
// inject the reference
const dataViewIdRef = state.references?.find(
(ref) => ref.name === FIELD_LIST_DATA_VIEW_REF_NAME
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,24 @@
import { apiCanAddNewPanel } from '@kbn/presentation-containers';
import { EmbeddableApiContext } from '@kbn/presentation-publishing';
import { IncompatibleActionError, UiActionsStart } from '@kbn/ui-actions-plugin/public';
import { addPanelGrouping } from '../add_panel_grouping';
import { embeddableExamplesGrouping } from '../embeddable_examples_grouping';
import { ADD_SEARCH_ACTION_ID, SEARCH_EMBEDDABLE_ID } from './constants';
import { SearchSerializedState } from './types';

export const registerAddSearchPanelAction = (uiActions: UiActionsStart) => {
uiActions.registerAction<EmbeddableApiContext>({
id: ADD_SEARCH_ACTION_ID,
grouping: [addPanelGrouping],
grouping: [embeddableExamplesGrouping],
getDisplayName: () => 'Search example',
getIconType: () => 'search',
isCompatible: async ({ embeddable }) => {
return apiCanAddNewPanel(embeddable);
},
execute: async ({ embeddable }) => {
if (!apiCanAddNewPanel(embeddable)) throw new IncompatibleActionError();
embeddable.addNewPanel(
embeddable.addNewPanel<SearchSerializedState>(
{
panelType: SEARCH_EMBEDDABLE_ID,
initialState: {},
},
true
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import React, { useEffect, useMemo } from 'react';
import { BehaviorSubject } from 'rxjs';
import { TimeRange } from '@kbn/es-query';
import { ReactEmbeddableRenderer } from '@kbn/embeddable-plugin/public';
import type { Api, State } from './types';
import type { SearchApi, SearchSerializedState } from './types';
import { SEARCH_EMBEDDABLE_ID } from './constants';

interface Props {
Expand Down Expand Up @@ -42,7 +42,7 @@ export function SearchEmbeddableRenderer(props: Props) {

return (
<div className="mapEmbeddableContainer">
<ReactEmbeddableRenderer<State, Api>
<ReactEmbeddableRenderer<SearchSerializedState, SearchApi>
type={SEARCH_EMBEDDABLE_ID}
state={initialState}
parentApi={parentApi}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,37 +6,50 @@
* Side Public License, v 1.
*/

import { EuiCallOut } from '@elastic/eui';
import { EuiBadge, EuiStat } from '@elastic/eui';
import { css } from '@emotion/react';
import { DataView } from '@kbn/data-views-plugin/common';
import { ReactEmbeddableFactory } from '@kbn/embeddable-plugin/public';
import { i18n } from '@kbn/i18n';
import {
initializeTimeRange,
fetch$,
initializeTimeRange,
useBatchedPublishingSubjects,
} from '@kbn/presentation-publishing';
import { euiThemeVars } from '@kbn/ui-theme';
import React, { useEffect } from 'react';
import { BehaviorSubject, switchMap, tap } from 'rxjs';
import { SEARCH_EMBEDDABLE_ID } from './constants';
import { getCount } from './get_count';
import { Api, Services, State } from './types';
import { SearchApi, Services, SearchSerializedState } from './types';

export const getSearchEmbeddableFactory = (services: Services) => {
const factory: ReactEmbeddableFactory<State, Api> = {
const factory: ReactEmbeddableFactory<SearchSerializedState, SearchApi> = {
type: SEARCH_EMBEDDABLE_ID,
deserializeState: (state) => {
return state.rawState as State;
},
deserializeState: (state) => state.rawState,
buildEmbeddable: async (state, buildApi, uuid, parentApi) => {
const timeRange = initializeTimeRange(state);
const defaultDataView = await services.dataViews.getDefaultDataView();
const dataViews$ = new BehaviorSubject<DataView[] | undefined>(
defaultDataView ? [defaultDataView] : undefined
);
const dataLoading$ = new BehaviorSubject<boolean | undefined>(false);
const blockingError$ = new BehaviorSubject<Error | undefined>(undefined);

if (!defaultDataView) {
blockingError$.next(
new Error(
i18n.translate('embeddableExamples.search.noDataViewError', {
defaultMessage: 'Please install a data view to view this example',
})
)
);
}

const api = buildApi(
{
...timeRange.api,
blockingError: blockingError$,
dataViews: dataViews$,
dataLoading: dataLoading$,
serializeState: () => {
Expand All @@ -53,7 +66,6 @@ export const getSearchEmbeddableFactory = (services: Services) => {
}
);

const error$ = new BehaviorSubject<Error | undefined>(undefined);
const count$ = new BehaviorSubject<number>(0);
let prevRequestAbortController: AbortController | undefined;
const fetchSubscription = fetch$(api)
Expand All @@ -64,7 +76,7 @@ export const getSearchEmbeddableFactory = (services: Services) => {
}
}),
switchMap(async (fetchContext) => {
error$.next(undefined);
blockingError$.next(undefined);
if (!defaultDataView) {
return;
}
Expand Down Expand Up @@ -103,41 +115,52 @@ export const getSearchEmbeddableFactory = (services: Services) => {
count$.next(next.count);
}
if (next && next.hasOwnProperty('error')) {
error$.next(next.error);
blockingError$.next(next.error);
}
});

return {
api,
Component: () => {
const [count, error] = useBatchedPublishingSubjects(count$, error$);
const [count, error] = useBatchedPublishingSubjects(count$, blockingError$);

useEffect(() => {
return () => {
fetchSubscription.unsubscribe();
};
}, []);

if (!defaultDataView) {
return (
<EuiCallOut title="Default data view not found" color="warning" iconType="warning">
<p>Please install a sample data set to run example.</p>
</EuiCallOut>
);
}

if (error) {
return (
<EuiCallOut title="Search error" color="warning" iconType="warning">
<p>{error.message}</p>
</EuiCallOut>
);
}
// in error case we can return null because the panel will handle rendering the blocking error.
if (error || !defaultDataView) return null;

return (
<p>
Found <strong>{count}</strong> from {defaultDataView.name}
</p>
<div
css={css`
width: 100%;
padding: ${euiThemeVars.euiSizeM};
`}
>
<EuiStat
title={count}
titleColor="subdued"
description={
<span>
<EuiBadge iconType="index" color="hollow">
{i18n.translate('embeddableExamples.search.dataViewName', {
defaultMessage: '{dataViewName}',
values: { dataViewName: defaultDataView.name },
})}
</EuiBadge>
</span>
}
titleSize="l"
>
{i18n.translate('embeddableExamples.search.result', {
defaultMessage: '{count, plural, one {document} other {documents}} found',
values: { count },
})}
</EuiStat>
</div>
);
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,18 @@
import { DataPublicPluginStart } from '@kbn/data-plugin/public';
import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
import type { DefaultEmbeddableApi } from '@kbn/embeddable-plugin/public';
import { TimeRange } from '@kbn/es-query';
import {
HasParentApi,
PublishesDataLoading,
PublishesDataViews,
PublishesUnifiedSearch,
PublishesWritableUnifiedSearch,
SerializedTimeRange,
} from '@kbn/presentation-publishing';

export interface State {
/*
* Time range only applied to this embeddable, overrides parentApi.timeRange$
*/
timeRange: TimeRange | undefined;
}
export type SearchSerializedState = SerializedTimeRange;

export type Api = DefaultEmbeddableApi<State> &
export type SearchApi = DefaultEmbeddableApi<SearchSerializedState> &
PublishesDataViews &
PublishesDataLoading &
Pick<PublishesWritableUnifiedSearch, 'timeRange$' | 'setTimeRange'> &
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import { PanelPackage } from './presentation_container';
* This API can add a new panel as a child.
*/
export interface CanAddNewPanel {
addNewPanel: <ApiType extends unknown = unknown>(
panel: PanelPackage,
addNewPanel: <SerializedState extends object, ApiType extends unknown = unknown>(
panel: PanelPackage<SerializedState>,
displaySuccessMessage?: boolean
) => Promise<ApiType | undefined>;
}
Expand Down
Loading

0 comments on commit ef81e02

Please sign in to comment.