mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -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 { 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>
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
};
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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>
|
||||
) : (
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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$ }
|
||||
),
|
||||
|
|
|
@ -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$ }
|
||||
),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue