[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:
Devon Thomson 2023-02-23 13:45:20 -06:00 committed by GitHub
parent fa6c0d1c31
commit f77f924a2a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 346 additions and 197 deletions

View 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>
</>
);
};

View file

@ -16,6 +16,7 @@ import { ControlsExampleStartDeps } from './plugin';
import { BasicReduxExample } from './basic_redux_example';
import { EditExample } from './edit_example';
import { SearchExample } from './search_example';
import { AddButtonExample } from './add_button_example';
export const renderApp = async (
{ data, navigation }: ControlsExampleStartDeps,
@ -30,6 +31,8 @@ export const renderApp = async (
<EditExample />
<EuiSpacer size="xl" />
<BasicReduxExample dataViewId={dataViews[0].id!} />
<EuiSpacer size="xl" />
<AddButtonExample dataViewId={dataViews[0].id!} />
</>
) : (
<div>{'Install web logs sample data to run controls examples.'}</div>

View file

@ -80,7 +80,7 @@ export const BasicReduxExample = ({ dataViewId }: { dataViewId: string }) => {
onLoadComplete={async (newControlGroup) => {
setControlGroup(newControlGroup);
}}
getInitialInput={async (initialInput, builder) => {
getCreationOptions={async (initialInput, builder) => {
await builder.addDataControlFromField(initialInput, {
dataViewId,
title: 'Destintion country',
@ -96,8 +96,10 @@ export const BasicReduxExample = ({ dataViewId }: { dataViewId: string }) => {
title: 'Bytes',
});
return {
...initialInput,
viewMode: ViewMode.VIEW,
initialInput: {
...initialInput,
viewMode: ViewMode.VIEW,
},
};
}}
/>

View file

@ -104,12 +104,14 @@ export const EditExample = () => {
</>
) : null}
<ControlGroupRenderer
getInitialInput={async (initialInput, builder) => {
getCreationOptions={async (initialInput, builder) => {
const persistedInput = await onLoad();
return {
...initialInput,
...persistedInput,
viewMode: ViewMode.EDIT,
initialInput: {
...initialInput,
...persistedInput,
viewMode: ViewMode.EDIT,
},
};
}}
onLoadComplete={async (newControlGroup) => {

View file

@ -133,7 +133,7 @@ export const SearchExample = ({ data, dataView, navigation }: Props) => {
/>
<ControlGroupRenderer
filters={filters}
getInitialInput={async (initialInput, builder) => {
getCreationOptions={async (initialInput, builder) => {
await builder.addDataControlFromField(initialInput, {
dataViewId: dataView.id!,
title: 'Destintion country',
@ -149,8 +149,10 @@ export const SearchExample = ({ data, dataView, navigation }: Props) => {
title: 'Bytes',
});
return {
...initialInput,
viewMode: ViewMode.VIEW,
initialInput: {
...initialInput,
viewMode: ViewMode.VIEW,
},
};
}}
onLoadComplete={async (newControlGroup) => {

View file

@ -8,7 +8,7 @@
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 classNames from 'classnames';
import {
@ -30,19 +30,15 @@ import {
} from '@dnd-kit/core';
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 { useControlGroupContainerContext } from '../control_group_renderer';
import { ControlGroupStrings } from '../control_group_strings';
export const ControlGroup = () => {
// Redux embeddable container Context
const reduxContext = useReduxEmbeddableContext<
ControlGroupReduxState,
typeof controlGroupReducers
>();
const reduxContext = useControlGroupContainerContext();
const {
embeddableInstance: controlGroup,
actions: { setControlOrders },
useEmbeddableSelector: select,
useEmbeddableDispatch,
@ -53,6 +49,7 @@ export const ControlGroup = () => {
const panels = select((state) => state.explicitInput.panels);
const viewMode = select((state) => state.explicitInput.viewMode);
const controlStyle = select((state) => state.explicitInput.controlStyle);
const showAddButton = select((state) => state.componentState.showAddButton);
const isEditable = viewMode === ViewMode.EDIT;
@ -101,7 +98,7 @@ export const ControlGroup = () => {
return (
<>
{idsInOrder.length > 0 ? (
{idsInOrder.length > 0 || showAddButton ? (
<EuiPanel
borderRadius="m"
color={panelBg}
@ -159,6 +156,18 @@ export const ControlGroup = () => {
</DragOverlay>
</DndContext>
</EuiFlexItem>
{showAddButton && (
<EuiFlexItem grow={false}>
<EuiButtonIcon
size="s"
iconSize="m"
display="base"
iconType={'plusInCircle'}
aria-label={ControlGroupStrings.management.getAddControlTitle()}
onClick={() => controlGroup.openAddDataControlFlyout()}
/>
</EuiFlexItem>
)}
</EuiFlexGroup>
</EuiPanel>
) : (

View file

@ -11,29 +11,31 @@ import { isEqual } from 'lodash';
import useLifecycles from 'react-use/lib/useLifecycles';
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 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 {
ControlGroupCreationOptions,
ControlGroupInput,
ControlGroupOutput,
ControlGroupReduxState,
CONTROL_GROUP_TYPE,
} 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 { controlGroupInputBuilder } from './control_group_input_builder';
import { ControlGroupContainer } from './embeddable/control_group_container';
import { ControlGroupContainerFactory } from './embeddable/control_group_container_factory';
export interface ControlGroupRendererProps {
filters?: Filter[];
getInitialInput: (
getCreationOptions: (
initialInput: Partial<ControlGroupInput>,
builder: typeof controlGroupInputBuilder
) => Promise<Partial<ControlGroupInput>>;
) => Promise<ControlGroupCreationOptions>;
onLoadComplete?: (controlGroup: ControlGroupContainer) => void;
timeRange?: TimeRange;
query?: Query;
@ -41,7 +43,7 @@ export interface ControlGroupRendererProps {
export const ControlGroupRenderer = ({
onLoadComplete,
getInitialInput,
getCreationOptions,
filters,
timeRange,
query,
@ -57,16 +59,26 @@ export const ControlGroupRenderer = ({
() => {
const { embeddable } = pluginServices.getServices();
(async () => {
const factory = embeddable.getEmbeddableFactory<
const factory = embeddable.getEmbeddableFactory(CONTROL_GROUP_TYPE) as EmbeddableFactory<
ControlGroupInput,
ControlGroupOutput,
IEmbeddable<ControlGroupInput, ControlGroupOutput>
>(CONTROL_GROUP_TYPE);
const newControlGroup = (await factory?.create({
id,
...getDefaultControlGroupInput(),
...(await getInitialInput(getDefaultControlGroupInput(), controlGroupInputBuilder)),
})) as ControlGroupContainer;
ControlGroupContainer
> & {
create: ControlGroupContainerFactory['create'];
};
const { initialInput, settings } = await getCreationOptions(
getDefaultControlGroupInput(),
controlGroupInputBuilder
);
const newControlGroup = (await factory?.create(
{
id,
...getDefaultControlGroupInput(),
...initialInput,
},
undefined,
settings
)) as ControlGroupContainer;
if (controlGroupRef.current) {
newControlGroup.render(controlGroupRef.current);
@ -105,7 +117,11 @@ export const ControlGroupRenderer = ({
};
export const useControlGroupContainerContext = () =>
useReduxEmbeddableContext<ControlGroupReduxState, typeof controlGroupReducers>();
useReduxEmbeddableContext<
ControlGroupReduxState,
typeof controlGroupReducers,
ControlGroupContainer
>();
// required for dynamic import using React.lazy()
// eslint-disable-next-line import/no-default-export

View file

@ -53,6 +53,7 @@ import {
import { CONTROL_WIDTH_OPTIONS } from './editor_constants';
import { pluginServices } from '../../services';
import { getDataControlFieldRegistry } from './data_control_editor_tools';
import { useControlGroupContainerContext } from '../control_group_renderer';
interface EditControlProps {
embeddable?: ControlEmbeddable<DataControlInput>;
isCreate: boolean;
@ -99,6 +100,10 @@ export const ControlEditor = ({
dataViews: { getIdsWithTitle, getDefaultId, get },
controls: { getControlFactory },
} = pluginServices.getServices();
const { useEmbeddableSelector: select } = useControlGroupContainerContext();
const editorConfig = select((state) => state.componentState.editorConfig);
const [state, setState] = useState<ControlEditorState>({
dataViewListItems: [],
});
@ -170,27 +175,29 @@ export const ControlEditor = ({
</EuiFlyoutHeader>
<EuiFlyoutBody data-test-subj="control-editor-flyout">
<EuiForm>
<EuiFormRow label={ControlGroupStrings.manageControl.getDataViewTitle()}>
<DataViewPicker
dataViews={state.dataViewListItems}
selectedDataViewId={dataView?.id}
onChangeDataViewId={(dataViewId) => {
setLastUsedDataViewId?.(dataViewId);
if (dataViewId === dataView?.id) return;
{!editorConfig?.hideDataViewSelector && (
<EuiFormRow label={ControlGroupStrings.manageControl.getDataViewTitle()}>
<DataViewPicker
dataViews={state.dataViewListItems}
selectedDataViewId={dataView?.id}
onChangeDataViewId={(dataViewId) => {
setLastUsedDataViewId?.(dataViewId);
if (dataViewId === dataView?.id) return;
onTypeEditorChange({ dataViewId });
setSelectedField(undefined);
get(dataViewId).then((newDataView) => {
setState((s) => ({ ...s, selectedDataView: newDataView }));
});
}}
trigger={{
label:
state.selectedDataView?.getName() ??
ControlGroupStrings.manageControl.getSelectDataViewMessage(),
}}
/>
</EuiFormRow>
onTypeEditorChange({ dataViewId });
setSelectedField(undefined);
get(dataViewId).then((newDataView) => {
setState((s) => ({ ...s, selectedDataView: newDataView }));
});
}}
trigger={{
label:
state.selectedDataView?.getName() ??
ControlGroupStrings.manageControl.getSelectDataViewMessage(),
}}
/>
</EuiFormRow>
)}
<EuiFormRow label={ControlGroupStrings.manageControl.getFieldTitle()}>
<FieldPicker
filterPredicate={(field: DataViewField) => {
@ -239,44 +246,48 @@ export const ControlEditor = ({
}}
/>
</EuiFormRow>
<EuiFormRow label={ControlGroupStrings.manageControl.getWidthInputTitle()}>
<>
<EuiButtonGroup
color="primary"
legend={ControlGroupStrings.management.controlWidth.getWidthSwitchLegend()}
options={CONTROL_WIDTH_OPTIONS}
idSelected={currentWidth}
onChange={(newWidth: string) => {
setCurrentWidth(newWidth as ControlWidth);
updateWidth(newWidth as ControlWidth);
}}
/>
{updateGrow && (
<>
<EuiSpacer size="s" />
<EuiSwitch
label={ControlGroupStrings.manageControl.getGrowSwitchTitle()}
color="primary"
checked={currentGrow}
onChange={() => {
setCurrentGrow(!currentGrow);
updateGrow(!currentGrow);
}}
data-test-subj="control-editor-grow-switch"
/>
</>
)}
</>
</EuiFormRow>
{CustomSettings && (factory as IEditableControlFactory).controlEditorOptionsComponent && (
<EuiFormRow label={ControlGroupStrings.manageControl.getControlSettingsTitle()}>
<CustomSettings
onChange={onTypeEditorChange}
initialInput={embeddable?.getInput()}
fieldType={fieldRegistry[selectedField].field.type}
/>
{!editorConfig?.hideWidthSettings && (
<EuiFormRow label={ControlGroupStrings.manageControl.getWidthInputTitle()}>
<>
<EuiButtonGroup
color="primary"
legend={ControlGroupStrings.management.controlWidth.getWidthSwitchLegend()}
options={CONTROL_WIDTH_OPTIONS}
idSelected={currentWidth}
onChange={(newWidth: string) => {
setCurrentWidth(newWidth as ControlWidth);
updateWidth(newWidth as ControlWidth);
}}
/>
{updateGrow && (
<>
<EuiSpacer size="s" />
<EuiSwitch
label={ControlGroupStrings.manageControl.getGrowSwitchTitle()}
color="primary"
checked={currentGrow}
onChange={() => {
setCurrentGrow(!currentGrow);
updateGrow(!currentGrow);
}}
data-test-subj="control-editor-grow-switch"
/>
</>
)}
</>
</EuiFormRow>
)}
{!editorConfig?.hideAdditionalSettings &&
CustomSettings &&
(factory as IEditableControlFactory).controlEditorOptionsComponent && (
<EuiFormRow label={ControlGroupStrings.manageControl.getControlSettingsTitle()}>
<CustomSettings
onChange={onTypeEditorChange}
initialInput={embeddable?.getInput()}
fieldType={fieldRegistry[selectedField].field.type}
/>
</EuiFormRow>
)}
{removeControl && (
<>
<EuiSpacer size="l" />

View file

@ -13,19 +13,18 @@ import React, { useEffect, useRef } from 'react';
import { OverlayRef } from '@kbn/core/public';
import { toMountPoint } from '@kbn/kibana-react-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 {
IEditableControlFactory,
ControlInput,
DataControlInput,
ControlEmbeddable,
IEditableControlFactory,
} from '../../types';
import { controlGroupReducers } from '../state/control_group_reducers';
import { ControlGroupContainer, setFlyoutRef } from '../embeddable/control_group_container';
import { pluginServices } from '../../services';
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 {
type: string;
@ -40,11 +39,7 @@ export const EditControlButton = ({ embeddableId }: { embeddableId: string }) =>
theme: { theme$ },
} = pluginServices.getServices();
// Redux embeddable container Context
const reduxContext = useReduxEmbeddableContext<
ControlGroupReduxState,
typeof controlGroupReducers,
ControlGroupContainer
>();
const reduxContext = useControlGroupContainerContext();
const {
embeddableInstance: controlGroup,
actions: { setControlWidth, setControlGrow },
@ -126,41 +121,45 @@ export const EditControlButton = ({ embeddableId }: { embeddableId: string }) =>
ref.close();
};
const ReduxWrapper = controlGroup.getReduxEmbeddableTools().Wrapper;
const flyoutInstance = openFlyout(
toMountPoint(
<ControlsServicesProvider>
<ControlEditor
isCreate={false}
width={panel.width}
grow={panel.grow}
embeddable={embeddable}
title={embeddable.getTitle()}
onCancel={() => onCancel(flyoutInstance)}
updateTitle={(newTitle) => (inputToReturn.title = newTitle)}
setLastUsedDataViewId={(lastUsed) => controlGroup.setLastUsedDataViewId(lastUsed)}
updateWidth={(newWidth) =>
dispatch(setControlWidth({ width: newWidth, embeddableId }))
}
updateGrow={(grow) => dispatch(setControlGrow({ grow, embeddableId }))}
onTypeEditorChange={(partialInput) => {
inputToReturn = { ...inputToReturn, ...partialInput };
}}
onSave={(type) => onSave(flyoutInstance, type)}
removeControl={() => {
openConfirm(ControlGroupStrings.management.deleteControls.getSubtitle(), {
confirmButtonText: ControlGroupStrings.management.deleteControls.getConfirm(),
cancelButtonText: ControlGroupStrings.management.deleteControls.getCancel(),
title: ControlGroupStrings.management.deleteControls.getDeleteTitle(),
buttonColor: 'danger',
}).then((confirmed) => {
if (confirmed) {
controlGroup.removeEmbeddable(embeddableId);
removed = true;
flyoutInstance.close();
}
});
}}
/>
<ReduxWrapper>
<ControlEditor
isCreate={false}
width={panel.width}
grow={panel.grow}
embeddable={embeddable}
title={embeddable.getTitle()}
onCancel={() => onCancel(flyoutInstance)}
updateTitle={(newTitle) => (inputToReturn.title = newTitle)}
setLastUsedDataViewId={(lastUsed) => controlGroup.setLastUsedDataViewId(lastUsed)}
updateWidth={(newWidth) =>
dispatch(setControlWidth({ width: newWidth, embeddableId }))
}
updateGrow={(grow) => dispatch(setControlGrow({ grow, embeddableId }))}
onTypeEditorChange={(partialInput) => {
inputToReturn = { ...inputToReturn, ...partialInput };
}}
onSave={(type) => onSave(flyoutInstance, type)}
removeControl={() => {
openConfirm(ControlGroupStrings.management.deleteControls.getSubtitle(), {
confirmButtonText: ControlGroupStrings.management.deleteControls.getConfirm(),
cancelButtonText: ControlGroupStrings.management.deleteControls.getCancel(),
title: ControlGroupStrings.management.deleteControls.getDeleteTitle(),
buttonColor: 'danger',
}).then((confirmed) => {
if (confirmed) {
controlGroup.removeEmbeddable(embeddableId);
removed = true;
flyoutInstance.close();
}
});
}}
/>
</ReduxWrapper>
</ControlsServicesProvider>,
{ theme$ }
),

View file

@ -31,6 +31,7 @@ export function openAddDataControlFlyout(this: ControlGroupContainer) {
theme: { theme$ },
} = pluginServices.getServices();
const ControlsServicesProvider = pluginServices.getContextProvider();
const ReduxWrapper = this.getReduxEmbeddableTools().Wrapper;
let controlInput: Partial<DataControlInput> = {};
const onCancel = () => {
@ -54,43 +55,45 @@ export function openAddDataControlFlyout(this: ControlGroupContainer) {
const flyoutInstance = openFlyout(
toMountPoint(
<ControlsServicesProvider>
<ControlEditor
setLastUsedDataViewId={(newId) => this.setLastUsedDataViewId(newId)}
getRelevantDataViewId={this.getMostRelevantDataViewId}
isCreate={true}
width={this.getInput().defaultControlWidth ?? DEFAULT_CONTROL_WIDTH}
grow={this.getInput().defaultControlGrow ?? DEFAULT_CONTROL_GROW}
updateTitle={(newTitle) => (controlInput.title = newTitle)}
updateWidth={(defaultControlWidth) => this.updateInput({ defaultControlWidth })}
updateGrow={(defaultControlGrow: boolean) => this.updateInput({ defaultControlGrow })}
onSave={(type) => {
this.closeAllFlyouts();
if (!type) {
return;
}
<ReduxWrapper>
<ControlEditor
setLastUsedDataViewId={(newId) => this.setLastUsedDataViewId(newId)}
getRelevantDataViewId={this.getMostRelevantDataViewId}
isCreate={true}
width={this.getInput().defaultControlWidth ?? DEFAULT_CONTROL_WIDTH}
grow={this.getInput().defaultControlGrow ?? DEFAULT_CONTROL_GROW}
updateTitle={(newTitle) => (controlInput.title = newTitle)}
updateWidth={(defaultControlWidth) => this.updateInput({ defaultControlWidth })}
updateGrow={(defaultControlGrow: boolean) => this.updateInput({ defaultControlGrow })}
onSave={(type) => {
this.closeAllFlyouts();
if (!type) {
return;
}
const factory = getControlFactory(type) as IEditableControlFactory;
if (factory.presaveTransformFunction) {
controlInput = factory.presaveTransformFunction(controlInput);
}
const factory = getControlFactory(type) as IEditableControlFactory;
if (factory.presaveTransformFunction) {
controlInput = factory.presaveTransformFunction(controlInput);
}
if (type === OPTIONS_LIST_CONTROL) {
this.addOptionsListControl(controlInput as AddOptionsListControlProps);
return;
}
if (type === OPTIONS_LIST_CONTROL) {
this.addOptionsListControl(controlInput as AddOptionsListControlProps);
return;
}
if (type === RANGE_SLIDER_CONTROL) {
this.addRangeSliderControl(controlInput as AddRangeSliderControlProps);
return;
}
if (type === RANGE_SLIDER_CONTROL) {
this.addRangeSliderControl(controlInput as AddRangeSliderControlProps);
return;
}
this.addDataControlFromField(controlInput as AddDataControlProps);
}}
onCancel={onCancel}
onTypeEditorChange={(partialInput) =>
(controlInput = { ...controlInput, ...partialInput })
}
/>
this.addDataControlFromField(controlInput as AddDataControlProps);
}}
onCancel={onCancel}
onTypeEditorChange={(partialInput) =>
(controlInput = { ...controlInput, ...partialInput })
}
/>
</ReduxWrapper>
</ControlsServicesProvider>,
{ theme$ }
),

View file

@ -21,6 +21,7 @@ import {
ControlGroupInput,
ControlGroupOutput,
ControlGroupReduxState,
ControlGroupSettings,
ControlPanelState,
ControlsPanels,
CONTROL_GROUP_TYPE,
@ -87,7 +88,9 @@ export class ControlGroupContainer extends Container<
};
public getMostRelevantDataViewId = () => {
return this.lastUsedDataViewId ?? this.relevantDataViewId;
const staticDataViewId =
this.getReduxEmbeddableTools().getState().componentState.staticDataViewId;
return staticDataViewId ?? this.lastUsedDataViewId ?? this.relevantDataViewId;
};
public getReduxEmbeddableTools = () => {
@ -134,7 +137,8 @@ export class ControlGroupContainer extends Container<
constructor(
reduxEmbeddablePackage: ReduxEmbeddablePackage,
initialInput: ControlGroupInput,
parent?: Container
parent?: Container,
settings?: ControlGroupSettings
) {
super(
initialInput,
@ -155,6 +159,7 @@ export class ControlGroupContainer extends Container<
>({
embeddable: this,
reducers: controlGroupReducers,
initialComponentState: settings,
});
// when all children are ready setup subscriptions

View file

@ -19,7 +19,7 @@ import { Container, EmbeddableFactoryDefinition } from '@kbn/embeddable-plugin/p
import { lazyLoadReduxEmbeddablePackage } from '@kbn/presentation-util-plugin/public';
import { EmbeddablePersistableStateService } from '@kbn/embeddable-plugin/common';
import { ControlGroupInput, CONTROL_GROUP_TYPE } from '../types';
import { ControlGroupInput, ControlGroupSettings, CONTROL_GROUP_TYPE } from '../types';
import {
createControlGroupExtract,
createControlGroupInject,
@ -49,9 +49,13 @@ export class ControlGroupContainerFactory implements EmbeddableFactoryDefinition
return getDefaultControlGroupInput();
}
public create = async (initialInput: ControlGroupInput, parent?: Container) => {
public create = async (
initialInput: ControlGroupInput,
parent?: Container,
settings?: ControlGroupSettings
) => {
const reduxEmbeddablePackage = await lazyLoadReduxEmbeddablePackage();
const { ControlGroupContainer } = await import('./control_group_container');
return new ControlGroupContainer(reduxEmbeddablePackage, initialInput, parent);
return new ControlGroupContainer(reduxEmbeddablePackage, initialInput, parent, settings);
};
}

View file

@ -15,7 +15,26 @@ export type ControlGroupOutput = ContainerOutput &
Omit<CommonControlOutput, 'dataViewId'> & { dataViewIds: string[] };
// 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 {
type ControlsPanels,

View file

@ -60,18 +60,20 @@ export const ControlsContent: React.FC<Props> = ({
return (
<LazyControlsRenderer
filters={filters}
getInitialInput={async () => ({
id: dataView.id ?? '',
type: CONTROL_GROUP_TYPE,
timeRange,
refreshConfig: REFRESH_CONFIG,
viewMode: ViewMode.VIEW,
filters: [...filters],
query,
chainingSystem: 'HIERARCHICAL',
controlStyle: 'oneLine',
defaultControlWidth: 'small',
panels: controlPanel,
getCreationOptions={async () => ({
initialInput: {
id: dataView.id ?? '',
type: CONTROL_GROUP_TYPE,
timeRange,
refreshConfig: REFRESH_CONFIG,
viewMode: ViewMode.VIEW,
filters: [...filters],
query,
chainingSystem: 'HIERARCHICAL',
controlStyle: 'oneLine',
defaultControlWidth: 'small',
panels: controlPanel,
},
})}
onLoadComplete={(newControlGroup) => {
setControlGroup(newControlGroup);

View file

@ -42,15 +42,17 @@ export class Timeslider extends Component<Props, {}> {
this._isMounted = true;
}
_getInitialInput = async (
_getCreationOptions = async (
initialInput: Partial<ControlGroupInput>,
builder: typeof controlGroupInputBuilder
) => {
builder.addTimeSliderControl(initialInput);
return {
...initialInput,
viewMode: ViewMode.VIEW,
timeRange: this.props.timeRange,
initialInput: {
...initialInput,
viewMode: ViewMode.VIEW,
timeRange: this.props.timeRange,
},
};
};
@ -91,7 +93,7 @@ export class Timeslider extends Component<Props, {}> {
<div className="mapTimeslider mapTimeslider--animation">
<ControlGroupRenderer
onLoadComplete={this._onLoadComplete}
getInitialInput={this._getInitialInput}
getCreationOptions={this._getCreationOptions}
timeRange={this.props.timeRange}
/>
</div>

View file

@ -277,7 +277,7 @@ const FilterGroupComponent = (props: PropsWithChildren<FilterGroupProps>) => {
});
});
return initialInput;
return { initialInput };
},
[dataViewId, timeRange, filters, chainingSystem, query, selectControlsWithPriority]
);
@ -336,7 +336,7 @@ const FilterGroupComponent = (props: PropsWithChildren<FilterGroupProps>) => {
<EuiFlexItem grow={true} data-test-subj="filter_group__items">
<ControlGroupRenderer
onLoadComplete={onControlGroupLoadHandler}
getInitialInput={setOptions}
getCreationOptions={setOptions}
/>
{!controlGroup ? <FilterGroupLoading /> : null}
</EuiFlexItem>