mirror of
https://github.com/elastic/kibana.git
synced 2025-06-28 11:05:39 -04:00
[Control Group] Add Button & Minimal Editor Settings (#151161)
Adds an "add" button that shows up to the right of the Control Group if configured. Also adds a system for fetching settings from consumers, and adds settings that can hide or show pieces of the Control editor flyout.
This commit is contained in:
parent
fa6c0d1c31
commit
f77f924a2a
16 changed files with 346 additions and 197 deletions
70
examples/controls_example/public/add_button_example.tsx
Normal file
70
examples/controls_example/public/add_button_example.tsx
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* 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 from 'react';
|
||||||
|
|
||||||
|
import { ViewMode } from '@kbn/embeddable-plugin/public';
|
||||||
|
import { withSuspense } from '@kbn/presentation-util-plugin/public';
|
||||||
|
import { EuiPanel, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
|
||||||
|
import { LazyControlGroupRenderer } from '@kbn/controls-plugin/public';
|
||||||
|
|
||||||
|
const ControlGroupRenderer = withSuspense(LazyControlGroupRenderer);
|
||||||
|
|
||||||
|
export const AddButtonExample = ({ dataViewId }: { dataViewId: string }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<EuiTitle>
|
||||||
|
<h2>Add button example</h2>
|
||||||
|
</EuiTitle>
|
||||||
|
<EuiText>
|
||||||
|
<p>
|
||||||
|
Use the built in add button to add controls to a control group based on a hardcoded
|
||||||
|
dataViewId and a simplified editor flyout
|
||||||
|
</p>
|
||||||
|
</EuiText>
|
||||||
|
<EuiSpacer size="m" />
|
||||||
|
<EuiPanel hasBorder={true}>
|
||||||
|
<ControlGroupRenderer
|
||||||
|
getCreationOptions={async (initialInput, builder) => {
|
||||||
|
await builder.addDataControlFromField(initialInput, {
|
||||||
|
dataViewId,
|
||||||
|
title: 'Destintion',
|
||||||
|
fieldName: 'geo.dest',
|
||||||
|
grow: false,
|
||||||
|
width: 'small',
|
||||||
|
});
|
||||||
|
await builder.addDataControlFromField(initialInput, {
|
||||||
|
dataViewId,
|
||||||
|
fieldName: 'geo.src',
|
||||||
|
grow: false,
|
||||||
|
title: 'Source',
|
||||||
|
width: 'small',
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
initialInput: {
|
||||||
|
...initialInput,
|
||||||
|
viewMode: ViewMode.EDIT,
|
||||||
|
defaultControlGrow: false,
|
||||||
|
defaultControlWidth: 'small',
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
showAddButton: true,
|
||||||
|
staticDataViewId: dataViewId,
|
||||||
|
editorConfig: {
|
||||||
|
hideAdditionalSettings: true,
|
||||||
|
hideDataViewSelector: true,
|
||||||
|
hideWidthSettings: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</EuiPanel>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
|
@ -16,6 +16,7 @@ import { ControlsExampleStartDeps } from './plugin';
|
||||||
import { BasicReduxExample } from './basic_redux_example';
|
import { BasicReduxExample } from './basic_redux_example';
|
||||||
import { EditExample } from './edit_example';
|
import { EditExample } from './edit_example';
|
||||||
import { SearchExample } from './search_example';
|
import { SearchExample } from './search_example';
|
||||||
|
import { AddButtonExample } from './add_button_example';
|
||||||
|
|
||||||
export const renderApp = async (
|
export const renderApp = async (
|
||||||
{ data, navigation }: ControlsExampleStartDeps,
|
{ data, navigation }: ControlsExampleStartDeps,
|
||||||
|
@ -30,6 +31,8 @@ export const renderApp = async (
|
||||||
<EditExample />
|
<EditExample />
|
||||||
<EuiSpacer size="xl" />
|
<EuiSpacer size="xl" />
|
||||||
<BasicReduxExample dataViewId={dataViews[0].id!} />
|
<BasicReduxExample dataViewId={dataViews[0].id!} />
|
||||||
|
<EuiSpacer size="xl" />
|
||||||
|
<AddButtonExample dataViewId={dataViews[0].id!} />
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div>{'Install web logs sample data to run controls examples.'}</div>
|
<div>{'Install web logs sample data to run controls examples.'}</div>
|
||||||
|
|
|
@ -80,7 +80,7 @@ export const BasicReduxExample = ({ dataViewId }: { dataViewId: string }) => {
|
||||||
onLoadComplete={async (newControlGroup) => {
|
onLoadComplete={async (newControlGroup) => {
|
||||||
setControlGroup(newControlGroup);
|
setControlGroup(newControlGroup);
|
||||||
}}
|
}}
|
||||||
getInitialInput={async (initialInput, builder) => {
|
getCreationOptions={async (initialInput, builder) => {
|
||||||
await builder.addDataControlFromField(initialInput, {
|
await builder.addDataControlFromField(initialInput, {
|
||||||
dataViewId,
|
dataViewId,
|
||||||
title: 'Destintion country',
|
title: 'Destintion country',
|
||||||
|
@ -96,8 +96,10 @@ export const BasicReduxExample = ({ dataViewId }: { dataViewId: string }) => {
|
||||||
title: 'Bytes',
|
title: 'Bytes',
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
|
initialInput: {
|
||||||
...initialInput,
|
...initialInput,
|
||||||
viewMode: ViewMode.VIEW,
|
viewMode: ViewMode.VIEW,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -104,12 +104,14 @@ export const EditExample = () => {
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
<ControlGroupRenderer
|
<ControlGroupRenderer
|
||||||
getInitialInput={async (initialInput, builder) => {
|
getCreationOptions={async (initialInput, builder) => {
|
||||||
const persistedInput = await onLoad();
|
const persistedInput = await onLoad();
|
||||||
return {
|
return {
|
||||||
|
initialInput: {
|
||||||
...initialInput,
|
...initialInput,
|
||||||
...persistedInput,
|
...persistedInput,
|
||||||
viewMode: ViewMode.EDIT,
|
viewMode: ViewMode.EDIT,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}}
|
}}
|
||||||
onLoadComplete={async (newControlGroup) => {
|
onLoadComplete={async (newControlGroup) => {
|
||||||
|
|
|
@ -133,7 +133,7 @@ export const SearchExample = ({ data, dataView, navigation }: Props) => {
|
||||||
/>
|
/>
|
||||||
<ControlGroupRenderer
|
<ControlGroupRenderer
|
||||||
filters={filters}
|
filters={filters}
|
||||||
getInitialInput={async (initialInput, builder) => {
|
getCreationOptions={async (initialInput, builder) => {
|
||||||
await builder.addDataControlFromField(initialInput, {
|
await builder.addDataControlFromField(initialInput, {
|
||||||
dataViewId: dataView.id!,
|
dataViewId: dataView.id!,
|
||||||
title: 'Destintion country',
|
title: 'Destintion country',
|
||||||
|
@ -149,8 +149,10 @@ export const SearchExample = ({ data, dataView, navigation }: Props) => {
|
||||||
title: 'Bytes',
|
title: 'Bytes',
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
|
initialInput: {
|
||||||
...initialInput,
|
...initialInput,
|
||||||
viewMode: ViewMode.VIEW,
|
viewMode: ViewMode.VIEW,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}}
|
}}
|
||||||
onLoadComplete={async (newControlGroup) => {
|
onLoadComplete={async (newControlGroup) => {
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import '../control_group.scss';
|
import '../control_group.scss';
|
||||||
|
|
||||||
import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui';
|
import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui';
|
||||||
import React, { useMemo, useState } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import {
|
import {
|
||||||
|
@ -30,19 +30,15 @@ import {
|
||||||
} from '@dnd-kit/core';
|
} from '@dnd-kit/core';
|
||||||
|
|
||||||
import { ViewMode } from '@kbn/embeddable-plugin/public';
|
import { ViewMode } from '@kbn/embeddable-plugin/public';
|
||||||
import { useReduxEmbeddableContext } from '@kbn/presentation-util-plugin/public';
|
|
||||||
|
|
||||||
import { ControlGroupReduxState } from '../types';
|
|
||||||
import { controlGroupReducers } from '../state/control_group_reducers';
|
|
||||||
import { ControlClone, SortableControl } from './control_group_sortable_item';
|
import { ControlClone, SortableControl } from './control_group_sortable_item';
|
||||||
|
import { useControlGroupContainerContext } from '../control_group_renderer';
|
||||||
|
import { ControlGroupStrings } from '../control_group_strings';
|
||||||
|
|
||||||
export const ControlGroup = () => {
|
export const ControlGroup = () => {
|
||||||
// Redux embeddable container Context
|
// Redux embeddable container Context
|
||||||
const reduxContext = useReduxEmbeddableContext<
|
const reduxContext = useControlGroupContainerContext();
|
||||||
ControlGroupReduxState,
|
|
||||||
typeof controlGroupReducers
|
|
||||||
>();
|
|
||||||
const {
|
const {
|
||||||
|
embeddableInstance: controlGroup,
|
||||||
actions: { setControlOrders },
|
actions: { setControlOrders },
|
||||||
useEmbeddableSelector: select,
|
useEmbeddableSelector: select,
|
||||||
useEmbeddableDispatch,
|
useEmbeddableDispatch,
|
||||||
|
@ -53,6 +49,7 @@ export const ControlGroup = () => {
|
||||||
const panels = select((state) => state.explicitInput.panels);
|
const panels = select((state) => state.explicitInput.panels);
|
||||||
const viewMode = select((state) => state.explicitInput.viewMode);
|
const viewMode = select((state) => state.explicitInput.viewMode);
|
||||||
const controlStyle = select((state) => state.explicitInput.controlStyle);
|
const controlStyle = select((state) => state.explicitInput.controlStyle);
|
||||||
|
const showAddButton = select((state) => state.componentState.showAddButton);
|
||||||
|
|
||||||
const isEditable = viewMode === ViewMode.EDIT;
|
const isEditable = viewMode === ViewMode.EDIT;
|
||||||
|
|
||||||
|
@ -101,7 +98,7 @@ export const ControlGroup = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{idsInOrder.length > 0 ? (
|
{idsInOrder.length > 0 || showAddButton ? (
|
||||||
<EuiPanel
|
<EuiPanel
|
||||||
borderRadius="m"
|
borderRadius="m"
|
||||||
color={panelBg}
|
color={panelBg}
|
||||||
|
@ -159,6 +156,18 @@ export const ControlGroup = () => {
|
||||||
</DragOverlay>
|
</DragOverlay>
|
||||||
</DndContext>
|
</DndContext>
|
||||||
</EuiFlexItem>
|
</EuiFlexItem>
|
||||||
|
{showAddButton && (
|
||||||
|
<EuiFlexItem grow={false}>
|
||||||
|
<EuiButtonIcon
|
||||||
|
size="s"
|
||||||
|
iconSize="m"
|
||||||
|
display="base"
|
||||||
|
iconType={'plusInCircle'}
|
||||||
|
aria-label={ControlGroupStrings.management.getAddControlTitle()}
|
||||||
|
onClick={() => controlGroup.openAddDataControlFlyout()}
|
||||||
|
/>
|
||||||
|
</EuiFlexItem>
|
||||||
|
)}
|
||||||
</EuiFlexGroup>
|
</EuiFlexGroup>
|
||||||
</EuiPanel>
|
</EuiPanel>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
@ -11,29 +11,31 @@ import { isEqual } from 'lodash';
|
||||||
import useLifecycles from 'react-use/lib/useLifecycles';
|
import useLifecycles from 'react-use/lib/useLifecycles';
|
||||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
|
||||||
import { IEmbeddable } from '@kbn/embeddable-plugin/public';
|
|
||||||
import { useReduxEmbeddableContext } from '@kbn/presentation-util-plugin/public';
|
|
||||||
import type { Filter, TimeRange, Query } from '@kbn/es-query';
|
|
||||||
import { compareFilters } from '@kbn/es-query';
|
import { compareFilters } from '@kbn/es-query';
|
||||||
|
import type { Filter, TimeRange, Query } from '@kbn/es-query';
|
||||||
|
import { EmbeddableFactory } from '@kbn/embeddable-plugin/public';
|
||||||
|
import { useReduxEmbeddableContext } from '@kbn/presentation-util-plugin/public';
|
||||||
|
|
||||||
import { pluginServices } from '../services';
|
|
||||||
import { getDefaultControlGroupInput } from '../../common';
|
|
||||||
import {
|
import {
|
||||||
|
ControlGroupCreationOptions,
|
||||||
ControlGroupInput,
|
ControlGroupInput,
|
||||||
ControlGroupOutput,
|
ControlGroupOutput,
|
||||||
ControlGroupReduxState,
|
ControlGroupReduxState,
|
||||||
CONTROL_GROUP_TYPE,
|
CONTROL_GROUP_TYPE,
|
||||||
} from './types';
|
} from './types';
|
||||||
import { ControlGroupContainer } from './embeddable/control_group_container';
|
import { pluginServices } from '../services';
|
||||||
|
import { getDefaultControlGroupInput } from '../../common';
|
||||||
import { controlGroupReducers } from './state/control_group_reducers';
|
import { controlGroupReducers } from './state/control_group_reducers';
|
||||||
import { controlGroupInputBuilder } from './control_group_input_builder';
|
import { controlGroupInputBuilder } from './control_group_input_builder';
|
||||||
|
import { ControlGroupContainer } from './embeddable/control_group_container';
|
||||||
|
import { ControlGroupContainerFactory } from './embeddable/control_group_container_factory';
|
||||||
|
|
||||||
export interface ControlGroupRendererProps {
|
export interface ControlGroupRendererProps {
|
||||||
filters?: Filter[];
|
filters?: Filter[];
|
||||||
getInitialInput: (
|
getCreationOptions: (
|
||||||
initialInput: Partial<ControlGroupInput>,
|
initialInput: Partial<ControlGroupInput>,
|
||||||
builder: typeof controlGroupInputBuilder
|
builder: typeof controlGroupInputBuilder
|
||||||
) => Promise<Partial<ControlGroupInput>>;
|
) => Promise<ControlGroupCreationOptions>;
|
||||||
onLoadComplete?: (controlGroup: ControlGroupContainer) => void;
|
onLoadComplete?: (controlGroup: ControlGroupContainer) => void;
|
||||||
timeRange?: TimeRange;
|
timeRange?: TimeRange;
|
||||||
query?: Query;
|
query?: Query;
|
||||||
|
@ -41,7 +43,7 @@ export interface ControlGroupRendererProps {
|
||||||
|
|
||||||
export const ControlGroupRenderer = ({
|
export const ControlGroupRenderer = ({
|
||||||
onLoadComplete,
|
onLoadComplete,
|
||||||
getInitialInput,
|
getCreationOptions,
|
||||||
filters,
|
filters,
|
||||||
timeRange,
|
timeRange,
|
||||||
query,
|
query,
|
||||||
|
@ -57,16 +59,26 @@ export const ControlGroupRenderer = ({
|
||||||
() => {
|
() => {
|
||||||
const { embeddable } = pluginServices.getServices();
|
const { embeddable } = pluginServices.getServices();
|
||||||
(async () => {
|
(async () => {
|
||||||
const factory = embeddable.getEmbeddableFactory<
|
const factory = embeddable.getEmbeddableFactory(CONTROL_GROUP_TYPE) as EmbeddableFactory<
|
||||||
ControlGroupInput,
|
ControlGroupInput,
|
||||||
ControlGroupOutput,
|
ControlGroupOutput,
|
||||||
IEmbeddable<ControlGroupInput, ControlGroupOutput>
|
ControlGroupContainer
|
||||||
>(CONTROL_GROUP_TYPE);
|
> & {
|
||||||
const newControlGroup = (await factory?.create({
|
create: ControlGroupContainerFactory['create'];
|
||||||
|
};
|
||||||
|
const { initialInput, settings } = await getCreationOptions(
|
||||||
|
getDefaultControlGroupInput(),
|
||||||
|
controlGroupInputBuilder
|
||||||
|
);
|
||||||
|
const newControlGroup = (await factory?.create(
|
||||||
|
{
|
||||||
id,
|
id,
|
||||||
...getDefaultControlGroupInput(),
|
...getDefaultControlGroupInput(),
|
||||||
...(await getInitialInput(getDefaultControlGroupInput(), controlGroupInputBuilder)),
|
...initialInput,
|
||||||
})) as ControlGroupContainer;
|
},
|
||||||
|
undefined,
|
||||||
|
settings
|
||||||
|
)) as ControlGroupContainer;
|
||||||
|
|
||||||
if (controlGroupRef.current) {
|
if (controlGroupRef.current) {
|
||||||
newControlGroup.render(controlGroupRef.current);
|
newControlGroup.render(controlGroupRef.current);
|
||||||
|
@ -105,7 +117,11 @@ export const ControlGroupRenderer = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useControlGroupContainerContext = () =>
|
export const useControlGroupContainerContext = () =>
|
||||||
useReduxEmbeddableContext<ControlGroupReduxState, typeof controlGroupReducers>();
|
useReduxEmbeddableContext<
|
||||||
|
ControlGroupReduxState,
|
||||||
|
typeof controlGroupReducers,
|
||||||
|
ControlGroupContainer
|
||||||
|
>();
|
||||||
|
|
||||||
// required for dynamic import using React.lazy()
|
// required for dynamic import using React.lazy()
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
|
|
@ -53,6 +53,7 @@ import {
|
||||||
import { CONTROL_WIDTH_OPTIONS } from './editor_constants';
|
import { CONTROL_WIDTH_OPTIONS } from './editor_constants';
|
||||||
import { pluginServices } from '../../services';
|
import { pluginServices } from '../../services';
|
||||||
import { getDataControlFieldRegistry } from './data_control_editor_tools';
|
import { getDataControlFieldRegistry } from './data_control_editor_tools';
|
||||||
|
import { useControlGroupContainerContext } from '../control_group_renderer';
|
||||||
interface EditControlProps {
|
interface EditControlProps {
|
||||||
embeddable?: ControlEmbeddable<DataControlInput>;
|
embeddable?: ControlEmbeddable<DataControlInput>;
|
||||||
isCreate: boolean;
|
isCreate: boolean;
|
||||||
|
@ -99,6 +100,10 @@ export const ControlEditor = ({
|
||||||
dataViews: { getIdsWithTitle, getDefaultId, get },
|
dataViews: { getIdsWithTitle, getDefaultId, get },
|
||||||
controls: { getControlFactory },
|
controls: { getControlFactory },
|
||||||
} = pluginServices.getServices();
|
} = pluginServices.getServices();
|
||||||
|
|
||||||
|
const { useEmbeddableSelector: select } = useControlGroupContainerContext();
|
||||||
|
const editorConfig = select((state) => state.componentState.editorConfig);
|
||||||
|
|
||||||
const [state, setState] = useState<ControlEditorState>({
|
const [state, setState] = useState<ControlEditorState>({
|
||||||
dataViewListItems: [],
|
dataViewListItems: [],
|
||||||
});
|
});
|
||||||
|
@ -170,6 +175,7 @@ export const ControlEditor = ({
|
||||||
</EuiFlyoutHeader>
|
</EuiFlyoutHeader>
|
||||||
<EuiFlyoutBody data-test-subj="control-editor-flyout">
|
<EuiFlyoutBody data-test-subj="control-editor-flyout">
|
||||||
<EuiForm>
|
<EuiForm>
|
||||||
|
{!editorConfig?.hideDataViewSelector && (
|
||||||
<EuiFormRow label={ControlGroupStrings.manageControl.getDataViewTitle()}>
|
<EuiFormRow label={ControlGroupStrings.manageControl.getDataViewTitle()}>
|
||||||
<DataViewPicker
|
<DataViewPicker
|
||||||
dataViews={state.dataViewListItems}
|
dataViews={state.dataViewListItems}
|
||||||
|
@ -191,6 +197,7 @@ export const ControlEditor = ({
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</EuiFormRow>
|
</EuiFormRow>
|
||||||
|
)}
|
||||||
<EuiFormRow label={ControlGroupStrings.manageControl.getFieldTitle()}>
|
<EuiFormRow label={ControlGroupStrings.manageControl.getFieldTitle()}>
|
||||||
<FieldPicker
|
<FieldPicker
|
||||||
filterPredicate={(field: DataViewField) => {
|
filterPredicate={(field: DataViewField) => {
|
||||||
|
@ -239,6 +246,7 @@ export const ControlEditor = ({
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</EuiFormRow>
|
</EuiFormRow>
|
||||||
|
{!editorConfig?.hideWidthSettings && (
|
||||||
<EuiFormRow label={ControlGroupStrings.manageControl.getWidthInputTitle()}>
|
<EuiFormRow label={ControlGroupStrings.manageControl.getWidthInputTitle()}>
|
||||||
<>
|
<>
|
||||||
<EuiButtonGroup
|
<EuiButtonGroup
|
||||||
|
@ -268,7 +276,10 @@ export const ControlEditor = ({
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
</EuiFormRow>
|
</EuiFormRow>
|
||||||
{CustomSettings && (factory as IEditableControlFactory).controlEditorOptionsComponent && (
|
)}
|
||||||
|
{!editorConfig?.hideAdditionalSettings &&
|
||||||
|
CustomSettings &&
|
||||||
|
(factory as IEditableControlFactory).controlEditorOptionsComponent && (
|
||||||
<EuiFormRow label={ControlGroupStrings.manageControl.getControlSettingsTitle()}>
|
<EuiFormRow label={ControlGroupStrings.manageControl.getControlSettingsTitle()}>
|
||||||
<CustomSettings
|
<CustomSettings
|
||||||
onChange={onTypeEditorChange}
|
onChange={onTypeEditorChange}
|
||||||
|
|
|
@ -13,19 +13,18 @@ import React, { useEffect, useRef } from 'react';
|
||||||
import { OverlayRef } from '@kbn/core/public';
|
import { OverlayRef } from '@kbn/core/public';
|
||||||
import { toMountPoint } from '@kbn/kibana-react-plugin/public';
|
import { toMountPoint } from '@kbn/kibana-react-plugin/public';
|
||||||
import { EmbeddableFactoryNotFoundError } from '@kbn/embeddable-plugin/public';
|
import { EmbeddableFactoryNotFoundError } from '@kbn/embeddable-plugin/public';
|
||||||
import { useReduxEmbeddableContext } from '@kbn/presentation-util-plugin/public';
|
|
||||||
import { ControlGroupReduxState } from '../types';
|
|
||||||
import { ControlEditor } from './control_editor';
|
|
||||||
import { pluginServices } from '../../services';
|
|
||||||
import { ControlGroupStrings } from '../control_group_strings';
|
|
||||||
import {
|
import {
|
||||||
IEditableControlFactory,
|
|
||||||
ControlInput,
|
ControlInput,
|
||||||
DataControlInput,
|
DataControlInput,
|
||||||
ControlEmbeddable,
|
ControlEmbeddable,
|
||||||
|
IEditableControlFactory,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
import { controlGroupReducers } from '../state/control_group_reducers';
|
import { pluginServices } from '../../services';
|
||||||
import { ControlGroupContainer, setFlyoutRef } from '../embeddable/control_group_container';
|
import { ControlEditor } from './control_editor';
|
||||||
|
import { ControlGroupStrings } from '../control_group_strings';
|
||||||
|
import { setFlyoutRef } from '../embeddable/control_group_container';
|
||||||
|
import { useControlGroupContainerContext } from '../control_group_renderer';
|
||||||
|
|
||||||
interface EditControlResult {
|
interface EditControlResult {
|
||||||
type: string;
|
type: string;
|
||||||
|
@ -40,11 +39,7 @@ export const EditControlButton = ({ embeddableId }: { embeddableId: string }) =>
|
||||||
theme: { theme$ },
|
theme: { theme$ },
|
||||||
} = pluginServices.getServices();
|
} = pluginServices.getServices();
|
||||||
// Redux embeddable container Context
|
// Redux embeddable container Context
|
||||||
const reduxContext = useReduxEmbeddableContext<
|
const reduxContext = useControlGroupContainerContext();
|
||||||
ControlGroupReduxState,
|
|
||||||
typeof controlGroupReducers,
|
|
||||||
ControlGroupContainer
|
|
||||||
>();
|
|
||||||
const {
|
const {
|
||||||
embeddableInstance: controlGroup,
|
embeddableInstance: controlGroup,
|
||||||
actions: { setControlWidth, setControlGrow },
|
actions: { setControlWidth, setControlGrow },
|
||||||
|
@ -126,9 +121,12 @@ export const EditControlButton = ({ embeddableId }: { embeddableId: string }) =>
|
||||||
ref.close();
|
ref.close();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ReduxWrapper = controlGroup.getReduxEmbeddableTools().Wrapper;
|
||||||
|
|
||||||
const flyoutInstance = openFlyout(
|
const flyoutInstance = openFlyout(
|
||||||
toMountPoint(
|
toMountPoint(
|
||||||
<ControlsServicesProvider>
|
<ControlsServicesProvider>
|
||||||
|
<ReduxWrapper>
|
||||||
<ControlEditor
|
<ControlEditor
|
||||||
isCreate={false}
|
isCreate={false}
|
||||||
width={panel.width}
|
width={panel.width}
|
||||||
|
@ -161,6 +159,7 @@ export const EditControlButton = ({ embeddableId }: { embeddableId: string }) =>
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
</ReduxWrapper>
|
||||||
</ControlsServicesProvider>,
|
</ControlsServicesProvider>,
|
||||||
{ theme$ }
|
{ theme$ }
|
||||||
),
|
),
|
||||||
|
|
|
@ -31,6 +31,7 @@ export function openAddDataControlFlyout(this: ControlGroupContainer) {
|
||||||
theme: { theme$ },
|
theme: { theme$ },
|
||||||
} = pluginServices.getServices();
|
} = pluginServices.getServices();
|
||||||
const ControlsServicesProvider = pluginServices.getContextProvider();
|
const ControlsServicesProvider = pluginServices.getContextProvider();
|
||||||
|
const ReduxWrapper = this.getReduxEmbeddableTools().Wrapper;
|
||||||
|
|
||||||
let controlInput: Partial<DataControlInput> = {};
|
let controlInput: Partial<DataControlInput> = {};
|
||||||
const onCancel = () => {
|
const onCancel = () => {
|
||||||
|
@ -54,6 +55,7 @@ export function openAddDataControlFlyout(this: ControlGroupContainer) {
|
||||||
const flyoutInstance = openFlyout(
|
const flyoutInstance = openFlyout(
|
||||||
toMountPoint(
|
toMountPoint(
|
||||||
<ControlsServicesProvider>
|
<ControlsServicesProvider>
|
||||||
|
<ReduxWrapper>
|
||||||
<ControlEditor
|
<ControlEditor
|
||||||
setLastUsedDataViewId={(newId) => this.setLastUsedDataViewId(newId)}
|
setLastUsedDataViewId={(newId) => this.setLastUsedDataViewId(newId)}
|
||||||
getRelevantDataViewId={this.getMostRelevantDataViewId}
|
getRelevantDataViewId={this.getMostRelevantDataViewId}
|
||||||
|
@ -91,6 +93,7 @@ export function openAddDataControlFlyout(this: ControlGroupContainer) {
|
||||||
(controlInput = { ...controlInput, ...partialInput })
|
(controlInput = { ...controlInput, ...partialInput })
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
</ReduxWrapper>
|
||||||
</ControlsServicesProvider>,
|
</ControlsServicesProvider>,
|
||||||
{ theme$ }
|
{ theme$ }
|
||||||
),
|
),
|
||||||
|
|
|
@ -21,6 +21,7 @@ import {
|
||||||
ControlGroupInput,
|
ControlGroupInput,
|
||||||
ControlGroupOutput,
|
ControlGroupOutput,
|
||||||
ControlGroupReduxState,
|
ControlGroupReduxState,
|
||||||
|
ControlGroupSettings,
|
||||||
ControlPanelState,
|
ControlPanelState,
|
||||||
ControlsPanels,
|
ControlsPanels,
|
||||||
CONTROL_GROUP_TYPE,
|
CONTROL_GROUP_TYPE,
|
||||||
|
@ -87,7 +88,9 @@ export class ControlGroupContainer extends Container<
|
||||||
};
|
};
|
||||||
|
|
||||||
public getMostRelevantDataViewId = () => {
|
public getMostRelevantDataViewId = () => {
|
||||||
return this.lastUsedDataViewId ?? this.relevantDataViewId;
|
const staticDataViewId =
|
||||||
|
this.getReduxEmbeddableTools().getState().componentState.staticDataViewId;
|
||||||
|
return staticDataViewId ?? this.lastUsedDataViewId ?? this.relevantDataViewId;
|
||||||
};
|
};
|
||||||
|
|
||||||
public getReduxEmbeddableTools = () => {
|
public getReduxEmbeddableTools = () => {
|
||||||
|
@ -134,7 +137,8 @@ export class ControlGroupContainer extends Container<
|
||||||
constructor(
|
constructor(
|
||||||
reduxEmbeddablePackage: ReduxEmbeddablePackage,
|
reduxEmbeddablePackage: ReduxEmbeddablePackage,
|
||||||
initialInput: ControlGroupInput,
|
initialInput: ControlGroupInput,
|
||||||
parent?: Container
|
parent?: Container,
|
||||||
|
settings?: ControlGroupSettings
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
initialInput,
|
initialInput,
|
||||||
|
@ -155,6 +159,7 @@ export class ControlGroupContainer extends Container<
|
||||||
>({
|
>({
|
||||||
embeddable: this,
|
embeddable: this,
|
||||||
reducers: controlGroupReducers,
|
reducers: controlGroupReducers,
|
||||||
|
initialComponentState: settings,
|
||||||
});
|
});
|
||||||
|
|
||||||
// when all children are ready setup subscriptions
|
// when all children are ready setup subscriptions
|
||||||
|
|
|
@ -19,7 +19,7 @@ import { Container, EmbeddableFactoryDefinition } from '@kbn/embeddable-plugin/p
|
||||||
import { lazyLoadReduxEmbeddablePackage } from '@kbn/presentation-util-plugin/public';
|
import { lazyLoadReduxEmbeddablePackage } from '@kbn/presentation-util-plugin/public';
|
||||||
import { EmbeddablePersistableStateService } from '@kbn/embeddable-plugin/common';
|
import { EmbeddablePersistableStateService } from '@kbn/embeddable-plugin/common';
|
||||||
|
|
||||||
import { ControlGroupInput, CONTROL_GROUP_TYPE } from '../types';
|
import { ControlGroupInput, ControlGroupSettings, CONTROL_GROUP_TYPE } from '../types';
|
||||||
import {
|
import {
|
||||||
createControlGroupExtract,
|
createControlGroupExtract,
|
||||||
createControlGroupInject,
|
createControlGroupInject,
|
||||||
|
@ -49,9 +49,13 @@ export class ControlGroupContainerFactory implements EmbeddableFactoryDefinition
|
||||||
return getDefaultControlGroupInput();
|
return getDefaultControlGroupInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
public create = async (initialInput: ControlGroupInput, parent?: Container) => {
|
public create = async (
|
||||||
|
initialInput: ControlGroupInput,
|
||||||
|
parent?: Container,
|
||||||
|
settings?: ControlGroupSettings
|
||||||
|
) => {
|
||||||
const reduxEmbeddablePackage = await lazyLoadReduxEmbeddablePackage();
|
const reduxEmbeddablePackage = await lazyLoadReduxEmbeddablePackage();
|
||||||
const { ControlGroupContainer } = await import('./control_group_container');
|
const { ControlGroupContainer } = await import('./control_group_container');
|
||||||
return new ControlGroupContainer(reduxEmbeddablePackage, initialInput, parent);
|
return new ControlGroupContainer(reduxEmbeddablePackage, initialInput, parent, settings);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,26 @@ export type ControlGroupOutput = ContainerOutput &
|
||||||
Omit<CommonControlOutput, 'dataViewId'> & { dataViewIds: string[] };
|
Omit<CommonControlOutput, 'dataViewId'> & { dataViewIds: string[] };
|
||||||
|
|
||||||
// public only - redux embeddable state type
|
// public only - redux embeddable state type
|
||||||
export type ControlGroupReduxState = ReduxEmbeddableState<ControlGroupInput, ControlGroupOutput>;
|
export type ControlGroupReduxState = ReduxEmbeddableState<
|
||||||
|
ControlGroupInput,
|
||||||
|
ControlGroupOutput,
|
||||||
|
ControlGroupSettings
|
||||||
|
>;
|
||||||
|
|
||||||
|
export interface ControlGroupCreationOptions {
|
||||||
|
initialInput?: Partial<ControlGroupInput>;
|
||||||
|
settings?: ControlGroupSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ControlGroupSettings {
|
||||||
|
showAddButton?: boolean;
|
||||||
|
staticDataViewId?: string;
|
||||||
|
editorConfig?: {
|
||||||
|
hideDataViewSelector?: boolean;
|
||||||
|
hideWidthSettings?: boolean;
|
||||||
|
hideAdditionalSettings?: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
type ControlsPanels,
|
type ControlsPanels,
|
||||||
|
|
|
@ -60,7 +60,8 @@ export const ControlsContent: React.FC<Props> = ({
|
||||||
return (
|
return (
|
||||||
<LazyControlsRenderer
|
<LazyControlsRenderer
|
||||||
filters={filters}
|
filters={filters}
|
||||||
getInitialInput={async () => ({
|
getCreationOptions={async () => ({
|
||||||
|
initialInput: {
|
||||||
id: dataView.id ?? '',
|
id: dataView.id ?? '',
|
||||||
type: CONTROL_GROUP_TYPE,
|
type: CONTROL_GROUP_TYPE,
|
||||||
timeRange,
|
timeRange,
|
||||||
|
@ -72,6 +73,7 @@ export const ControlsContent: React.FC<Props> = ({
|
||||||
controlStyle: 'oneLine',
|
controlStyle: 'oneLine',
|
||||||
defaultControlWidth: 'small',
|
defaultControlWidth: 'small',
|
||||||
panels: controlPanel,
|
panels: controlPanel,
|
||||||
|
},
|
||||||
})}
|
})}
|
||||||
onLoadComplete={(newControlGroup) => {
|
onLoadComplete={(newControlGroup) => {
|
||||||
setControlGroup(newControlGroup);
|
setControlGroup(newControlGroup);
|
||||||
|
|
|
@ -42,15 +42,17 @@ export class Timeslider extends Component<Props, {}> {
|
||||||
this._isMounted = true;
|
this._isMounted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
_getInitialInput = async (
|
_getCreationOptions = async (
|
||||||
initialInput: Partial<ControlGroupInput>,
|
initialInput: Partial<ControlGroupInput>,
|
||||||
builder: typeof controlGroupInputBuilder
|
builder: typeof controlGroupInputBuilder
|
||||||
) => {
|
) => {
|
||||||
builder.addTimeSliderControl(initialInput);
|
builder.addTimeSliderControl(initialInput);
|
||||||
return {
|
return {
|
||||||
|
initialInput: {
|
||||||
...initialInput,
|
...initialInput,
|
||||||
viewMode: ViewMode.VIEW,
|
viewMode: ViewMode.VIEW,
|
||||||
timeRange: this.props.timeRange,
|
timeRange: this.props.timeRange,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -91,7 +93,7 @@ export class Timeslider extends Component<Props, {}> {
|
||||||
<div className="mapTimeslider mapTimeslider--animation">
|
<div className="mapTimeslider mapTimeslider--animation">
|
||||||
<ControlGroupRenderer
|
<ControlGroupRenderer
|
||||||
onLoadComplete={this._onLoadComplete}
|
onLoadComplete={this._onLoadComplete}
|
||||||
getInitialInput={this._getInitialInput}
|
getCreationOptions={this._getCreationOptions}
|
||||||
timeRange={this.props.timeRange}
|
timeRange={this.props.timeRange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -277,7 +277,7 @@ const FilterGroupComponent = (props: PropsWithChildren<FilterGroupProps>) => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return initialInput;
|
return { initialInput };
|
||||||
},
|
},
|
||||||
[dataViewId, timeRange, filters, chainingSystem, query, selectControlsWithPriority]
|
[dataViewId, timeRange, filters, chainingSystem, query, selectControlsWithPriority]
|
||||||
);
|
);
|
||||||
|
@ -336,7 +336,7 @@ const FilterGroupComponent = (props: PropsWithChildren<FilterGroupProps>) => {
|
||||||
<EuiFlexItem grow={true} data-test-subj="filter_group__items">
|
<EuiFlexItem grow={true} data-test-subj="filter_group__items">
|
||||||
<ControlGroupRenderer
|
<ControlGroupRenderer
|
||||||
onLoadComplete={onControlGroupLoadHandler}
|
onLoadComplete={onControlGroupLoadHandler}
|
||||||
getInitialInput={setOptions}
|
getCreationOptions={setOptions}
|
||||||
/>
|
/>
|
||||||
{!controlGroup ? <FilterGroupLoading /> : null}
|
{!controlGroup ? <FilterGroupLoading /> : null}
|
||||||
</EuiFlexItem>
|
</EuiFlexItem>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue