[control group] implement lastUsedDataViewId$ (#190269)

PR added `lastUsedDataViewId$` to `ControlGroupApi`.
`lastUsedDataViewId$` is implemented in `initializeControlsManager`.

PR also cleaned up typings by removing `DataControlEditorState`.
`DataControlEditorState` was a weird smooshing together of
DefaultDataControlState and stuff used by the editor. Instead of
`DataControlEditorState`, values used by the editor are just passed in
as top level keys.

---------

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Nathan Reese 2024-08-15 09:59:10 -06:00 committed by GitHub
parent d24105d313
commit 3ad861e4b1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 211 additions and 105 deletions

View file

@ -139,7 +139,6 @@ export const ReactControlExample = ({
addNewPanel: () => {
return Promise.resolve(undefined);
},
lastUsedDataViewId: new BehaviorSubject<string>(WEB_LOGS_DATA_VIEW_ID),
saveNotification$,
reload$,
};

View file

@ -73,7 +73,13 @@ export const getControlGroupEmbeddableFactory = (services: {
} = initialRuntimeState;
const autoApplySelections$ = new BehaviorSubject<boolean>(autoApplySelections);
const controlsManager = initControlsManager(initialChildControlState);
const parentDataViewId = apiPublishesDataViews(parentApi)
? parentApi.dataViews.value?.[0]?.id
: undefined;
const controlsManager = initControlsManager(
initialChildControlState,
parentDataViewId ?? (await services.dataViews.getDefaultId())
);
const selectionsManager = initSelectionsManager({
...controlsManager.api,
autoApplySelections$,
@ -172,6 +178,7 @@ export const getControlGroupEmbeddableFactory = (services: {
initialState: {
grow: api.grow.getValue(),
width: api.width.getValue(),
dataViewId: controlsManager.api.lastUsedDataViewId$.value,
},
onSave: ({ type: controlType, state: initialState }) => {
api.addNewPanel({

View file

@ -7,19 +7,24 @@
*/
import { DefaultControlApi } from '../controls/types';
import { initControlsManager } from './init_controls_manager';
import { initControlsManager, getLastUsedDataViewId } from './init_controls_manager';
jest.mock('uuid', () => ({
v4: jest.fn().mockReturnValue('delta'),
}));
const DEFAULT_DATA_VIEW_ID = 'myDataView';
describe('PresentationContainer api', () => {
test('addNewPanel should add control at end of controls', async () => {
const controlsManager = initControlsManager({
alpha: { type: 'testControl', order: 0 },
bravo: { type: 'testControl', order: 1 },
charlie: { type: 'testControl', order: 2 },
});
const controlsManager = initControlsManager(
{
alpha: { type: 'testControl', order: 0 },
bravo: { type: 'testControl', order: 1 },
charlie: { type: 'testControl', order: 2 },
},
DEFAULT_DATA_VIEW_ID
);
const addNewPanelPromise = controlsManager.api.addNewPanel({
panelType: 'testControl',
initialState: {},
@ -35,11 +40,14 @@ describe('PresentationContainer api', () => {
});
test('removePanel should remove control', () => {
const controlsManager = initControlsManager({
alpha: { type: 'testControl', order: 0 },
bravo: { type: 'testControl', order: 1 },
charlie: { type: 'testControl', order: 2 },
});
const controlsManager = initControlsManager(
{
alpha: { type: 'testControl', order: 0 },
bravo: { type: 'testControl', order: 1 },
charlie: { type: 'testControl', order: 2 },
},
DEFAULT_DATA_VIEW_ID
);
controlsManager.api.removePanel('bravo');
expect(controlsManager.controlsInOrder$.value.map((element) => element.id)).toEqual([
'alpha',
@ -48,11 +56,14 @@ describe('PresentationContainer api', () => {
});
test('replacePanel should replace control', async () => {
const controlsManager = initControlsManager({
alpha: { type: 'testControl', order: 0 },
bravo: { type: 'testControl', order: 1 },
charlie: { type: 'testControl', order: 2 },
});
const controlsManager = initControlsManager(
{
alpha: { type: 'testControl', order: 0 },
bravo: { type: 'testControl', order: 1 },
charlie: { type: 'testControl', order: 2 },
},
DEFAULT_DATA_VIEW_ID
);
const replacePanelPromise = controlsManager.api.replacePanel('bravo', {
panelType: 'testControl',
initialState: {},
@ -68,10 +79,13 @@ describe('PresentationContainer api', () => {
describe('untilInitialized', () => {
test('should not resolve until all controls are initialized', async () => {
const controlsManager = initControlsManager({
alpha: { type: 'testControl', order: 0 },
bravo: { type: 'testControl', order: 1 },
});
const controlsManager = initControlsManager(
{
alpha: { type: 'testControl', order: 0 },
bravo: { type: 'testControl', order: 1 },
},
DEFAULT_DATA_VIEW_ID
);
let isDone = false;
controlsManager.api.untilInitialized().then(() => {
isDone = true;
@ -89,10 +103,13 @@ describe('PresentationContainer api', () => {
});
test('should resolve when all control already initialized ', async () => {
const controlsManager = initControlsManager({
alpha: { type: 'testControl', order: 0 },
bravo: { type: 'testControl', order: 1 },
});
const controlsManager = initControlsManager(
{
alpha: { type: 'testControl', order: 0 },
bravo: { type: 'testControl', order: 1 },
},
DEFAULT_DATA_VIEW_ID
);
controlsManager.setControlApi('alpha', {} as unknown as DefaultControlApi);
controlsManager.setControlApi('bravo', {} as unknown as DefaultControlApi);
@ -109,10 +126,13 @@ describe('PresentationContainer api', () => {
describe('snapshotControlsRuntimeState', () => {
test('should snapshot runtime state for all controls', async () => {
const controlsManager = initControlsManager({
alpha: { type: 'testControl', order: 1 },
bravo: { type: 'testControl', order: 0 },
});
const controlsManager = initControlsManager(
{
alpha: { type: 'testControl', order: 1 },
bravo: { type: 'testControl', order: 0 },
},
DEFAULT_DATA_VIEW_ID
);
controlsManager.setControlApi('alpha', {
snapshotRuntimeState: () => {
return { key1: 'alpha value' };
@ -137,3 +157,33 @@ describe('snapshotControlsRuntimeState', () => {
});
});
});
describe('getLastUsedDataViewId', () => {
test('should return last used data view id', () => {
const dataViewId = getLastUsedDataViewId(
[
{ id: 'alpha', type: 'testControl' },
{ id: 'bravo', type: 'testControl' },
{ id: 'charlie', type: 'testControl' },
],
{
alpha: { dataViewId: '1', type: 'testControl', order: 0 },
bravo: { dataViewId: '2', type: 'testControl', order: 1 },
charlie: { type: 'testControl', order: 2 },
}
);
expect(dataViewId).toBe('2');
});
test('should return undefined when there are no controls', () => {
const dataViewId = getLastUsedDataViewId([], {});
expect(dataViewId).toBeUndefined();
});
test('should return undefined when there are no controls with data views', () => {
const dataViewId = getLastUsedDataViewId([{ id: 'alpha', type: 'testControl' }], {
alpha: { type: 'testControl', order: 0 },
});
expect(dataViewId).toBeUndefined();
});
});

View file

@ -18,9 +18,10 @@ import { BehaviorSubject, first, merge } from 'rxjs';
import { PublishingSubject, StateComparators } from '@kbn/presentation-publishing';
import { omit } from 'lodash';
import { apiHasSnapshottableState } from '@kbn/presentation-containers/interfaces/serialized_state';
import { ControlPanelsState, ControlPanelState } from './types';
import { ControlGroupApi, ControlPanelsState, ControlPanelState } from './types';
import { DefaultControlApi, DefaultControlState } from '../controls/types';
import { ControlGroupComparatorState } from './control_group_unsaved_changes_api';
import { DefaultDataControlState } from '../controls/data_controls/types';
export type ControlsInOrder = Array<{ id: string; type: string }>;
@ -35,7 +36,10 @@ export function getControlsInOrder(initialControlPanelsState: ControlPanelsState
.map(({ id, type }) => ({ id, type })); // filter out `order`
}
export function initControlsManager(initialControlPanelsState: ControlPanelsState) {
export function initControlsManager(
initialControlPanelsState: ControlPanelsState,
defaultDataViewId: string | null
) {
const lastSavedControlsPanelState$ = new BehaviorSubject(initialControlPanelsState);
const initialControlIds = Object.keys(initialControlPanelsState);
const children$ = new BehaviorSubject<{ [key: string]: DefaultControlApi }>({});
@ -45,6 +49,11 @@ export function initControlsManager(initialControlPanelsState: ControlPanelsStat
const controlsInOrder$ = new BehaviorSubject<ControlsInOrder>(
getControlsInOrder(initialControlPanelsState)
);
const lastUsedDataViewId$ = new BehaviorSubject<string | undefined>(
getLastUsedDataViewId(controlsInOrder$.value, initialControlPanelsState) ??
defaultDataViewId ??
undefined
);
function untilControlLoaded(
id: string
@ -79,6 +88,9 @@ export function initControlsManager(initialControlPanelsState: ControlPanelsStat
{ panelType, initialState }: PanelPackage<DefaultControlState>,
index: number
) {
if ((initialState as DefaultDataControlState)?.dataViewId) {
lastUsedDataViewId$.next((initialState as DefaultDataControlState).dataViewId);
}
const id = generateId();
const nextControlsInOrder = [...controlsInOrder$.value];
nextControlsInOrder.splice(index, 0, {
@ -156,6 +168,7 @@ export function initControlsManager(initialControlPanelsState: ControlPanelsStat
return controlsRuntimeState;
},
api: {
lastUsedDataViewId$: lastUsedDataViewId$ as PublishingSubject<string | undefined>,
getSerializedStateForChild: (childId: string) => {
const controlPanelState = controlsPanelState[childId];
return controlPanelState ? { rawState: controlPanelState } : undefined;
@ -196,7 +209,8 @@ export function initControlsManager(initialControlPanelsState: ControlPanelsStat
});
},
} as PresentationContainer &
HasSerializedChildState<ControlPanelState> & { untilInitialized: () => Promise<void> },
HasSerializedChildState<ControlPanelState> &
Pick<ControlGroupApi, 'untilInitialized' | 'lastUsedDataViewId$'>,
comparators: {
controlsInOrder: [
controlsInOrder$,
@ -222,3 +236,21 @@ export function initControlsManager(initialControlPanelsState: ControlPanelsStat
>,
};
}
export function getLastUsedDataViewId(
controlsInOrder: ControlsInOrder,
initialControlPanelsState: ControlPanelsState<
ControlPanelState & Partial<DefaultDataControlState>
>
) {
let dataViewId: string | undefined;
for (let i = controlsInOrder.length - 1; i >= 0; i--) {
const controlId = controlsInOrder[i].id;
const controlState = initialControlPanelsState[controlId];
if (controlState?.dataViewId) {
dataViewId = controlState.dataViewId;
break;
}
}
return dataViewId;
}

View file

@ -73,6 +73,7 @@ export type ControlGroupApi = PresentationContainer &
openAddDataControlFlyout: (settings?: {
controlInputTransform?: ControlInputTransform;
}) => void;
lastUsedDataViewId$: PublishingSubject<string | undefined>;
};
export interface ControlGroupRuntimeState {

View file

@ -25,7 +25,6 @@ jest.mock('../../control_factory_registry', () => ({
import { DEFAULT_CONTROL_GROW, DEFAULT_CONTROL_WIDTH } from '../../../../common';
import { ControlGroupApi } from '../../control_group/types';
import { DataControlEditor } from './data_control_editor';
import { DataControlEditorState } from './open_data_control_editor';
import {
getMockedOptionsListControlFactory,
getMockedRangeSliderControlFactory,
@ -57,7 +56,6 @@ mockDataViews.get = jest.fn().mockResolvedValue(mockDataView);
const dashboardApi = {
timeRange$: new BehaviorSubject<TimeRange | undefined>(undefined),
lastUsedDataViewId$: new BehaviorSubject<string>(mockDataView.id!),
};
const controlGroupApi = {
parentApi: dashboardApi,
@ -68,8 +66,14 @@ const controlGroupApi = {
describe('Data control editor', () => {
const mountComponent = async ({
initialState,
controlId,
controlType,
initialDefaultPanelTitle,
}: {
initialState?: Partial<DataControlEditorState>;
initialState?: Partial<DefaultDataControlState>;
controlId?: string;
controlType?: string;
initialDefaultPanelTitle?: string;
}) => {
mockDataViews.get = jest.fn().mockResolvedValue(mockDataView);
@ -78,11 +82,14 @@ describe('Data control editor', () => {
<DataControlEditor
onCancel={() => {}}
onSave={() => {}}
parentApi={controlGroupApi}
controlGroupApi={controlGroupApi}
initialState={{
dataViewId: dashboardApi.lastUsedDataViewId$.getValue(),
dataViewId: mockDataView.id,
...initialState,
}}
controlId={controlId}
controlType={controlType}
initialDefaultPanelTitle={initialDefaultPanelTitle}
services={{ dataViews: mockDataViews }}
/>
</I18nProvider>
@ -238,11 +245,11 @@ describe('Data control editor', () => {
test('auto-fills input with the default title', async () => {
const controlEditor = await mountComponent({
initialState: {
controlType: 'optionsList',
controlId: 'testId',
fieldName: 'machine.os.raw',
defaultPanelTitle: 'OS',
},
controlType: 'optionsList',
controlId: 'testId',
initialDefaultPanelTitle: 'OS',
});
const titleInput = await controlEditor.findByTestId('control-editor-title-input');
expect(titleInput.getAttribute('value')).toBe('OS');
@ -252,11 +259,11 @@ describe('Data control editor', () => {
test('auto-fills input with the custom title', async () => {
const controlEditor = await mountComponent({
initialState: {
controlType: 'optionsList',
controlId: 'testId',
fieldName: 'machine.os.raw',
title: 'Custom title',
},
controlType: 'optionsList',
controlId: 'testId',
});
const titleInput = await controlEditor.findByTestId('control-editor-title-input');
expect(titleInput.getAttribute('value')).toBe('Custom title');
@ -267,10 +274,10 @@ describe('Data control editor', () => {
test('selects the provided control type', async () => {
const controlEditor = await mountComponent({
initialState: {
controlType: 'rangeSlider',
controlId: 'testId',
fieldName: 'bytes',
},
controlType: 'rangeSlider',
controlId: 'testId',
});
expect(controlEditor.getByTestId('create__optionsList')).toBeEnabled();

View file

@ -46,14 +46,18 @@ import { getAllControlTypes, getControlFactory } from '../../control_factory_reg
import { ControlGroupApi } from '../../control_group/types';
import { DataControlEditorStrings } from './data_control_constants';
import { getDataControlFieldRegistry } from './data_control_editor_utils';
import { DataControlEditorState } from './open_data_control_editor';
import { DataControlFactory, DefaultDataControlState, isDataControlFactory } from './types';
export interface ControlEditorProps<State extends DataControlEditorState = DataControlEditorState> {
initialState: State;
parentApi: ControlGroupApi; // controls must always have a parent API
onCancel: (newState: State) => void;
onSave: (newState: State, type: string) => void;
export interface ControlEditorProps<
State extends DefaultDataControlState = DefaultDataControlState
> {
initialState: Partial<State>;
controlType?: string;
controlId?: string;
initialDefaultPanelTitle?: string;
controlGroupApi: ControlGroupApi; // controls must always have a parent API
onCancel: (newState: Partial<State>) => void;
onSave: (newState: Partial<State>, type: string) => void;
services: {
dataViews: DataViewsPublicPluginStart;
};
@ -130,27 +134,27 @@ const CompatibleControlTypesComponent = ({
);
};
export const DataControlEditor = <State extends DataControlEditorState = DataControlEditorState>({
export const DataControlEditor = <State extends DefaultDataControlState = DefaultDataControlState>({
initialState,
controlId,
controlType,
initialDefaultPanelTitle,
onSave,
onCancel,
parentApi: controlGroup,
controlGroupApi,
/** TODO: These should not be props */
services: { dataViews: dataViewService },
}: ControlEditorProps<State>) => {
const [defaultGrow, defaultWidth] = useBatchedPublishingSubjects(
controlGroup.grow,
controlGroup.width
// controlGroup.parentApi?.lastUsedDataViewId, // TODO: Make this work
controlGroupApi.grow,
controlGroupApi.width
);
const [editorState, setEditorState] = useState<State>(initialState);
const [editorState, setEditorState] = useState<Partial<State>>(initialState);
const [defaultPanelTitle, setDefaultPanelTitle] = useState<string>(
initialState.defaultPanelTitle ?? initialState.fieldName ?? ''
initialDefaultPanelTitle ?? initialState.fieldName ?? ''
);
const [panelTitle, setPanelTitle] = useState<string>(initialState.title ?? defaultPanelTitle);
const [selectedControlType, setSelectedControlType] = useState<string | undefined>(
initialState.controlType
);
const [selectedControlType, setSelectedControlType] = useState<string | undefined>(controlType);
const [controlOptionsValid, setControlOptionsValid] = useState<boolean>(true);
/** TODO: Make `editorConfig` work when refactoring the `ControlGroupRenderer` */
@ -209,22 +213,22 @@ export const DataControlEditor = <State extends DataControlEditorState = DataCon
data-test-subj="control-editor-custom-settings"
>
<CustomSettings
initialState={initialState as DefaultDataControlState}
initialState={initialState}
field={fieldRegistry[editorState.fieldName].field}
updateState={(newState) => setEditorState({ ...editorState, ...newState })}
setControlEditorValid={setControlOptionsValid}
parentApi={controlGroup}
controlGroupApi={controlGroupApi}
/>
</EuiDescribedFormGroup>
);
}, [fieldRegistry, selectedControlType, initialState, editorState, controlGroup]);
}, [fieldRegistry, selectedControlType, initialState, editorState, controlGroupApi]);
return (
<>
<EuiFlyoutHeader hasBorder>
<EuiTitle size="m">
<h2>
{!initialState.controlId // if no ID, then we are creating a new control
{!controlId // if no ID, then we are creating a new control
? DataControlEditorStrings.manageControl.getFlyoutCreateTitle()
: DataControlEditorStrings.manageControl.getFlyoutEditTitle()}
</h2>
@ -385,7 +389,7 @@ export const DataControlEditor = <State extends DataControlEditorState = DataCon
{/* )} */}
</EuiDescribedFormGroup>
{CustomSettingsComponent}
{initialState.controlId && (
{controlId && (
<>
<EuiSpacer size="l" />
<EuiButtonEmpty
@ -395,7 +399,7 @@ export const DataControlEditor = <State extends DataControlEditorState = DataCon
color="danger"
onClick={() => {
onCancel(initialState); // don't want to show "lost changes" warning
controlGroup.removePanel(initialState.controlId!);
controlGroupApi.removePanel(controlId!);
}}
>
{DataControlEditorStrings.manageControl.getDeleteButtonTitle()}

View file

@ -36,7 +36,7 @@ export const initializeDataControl = <EditorState extends object = {}>(
* responsible for managing
*/
editorStateManager: ControlStateManager<EditorState>,
controlGroup: ControlGroupApi,
controlGroupApi: ControlGroupApi,
services: {
core: CoreStart;
dataViews: DataViewsPublicPluginStart;
@ -159,22 +159,24 @@ export const initializeDataControl = <EditorState extends object = {}>(
(Object.keys(initialState) as Array<keyof DefaultDataControlState & EditorState>).forEach(
(key) => {
if (!isEqual(mergedStateManager[key].getValue(), newState[key])) {
mergedStateManager[key].next(newState[key]);
mergedStateManager[key].next(
newState[key] as DefaultDataControlState & EditorState[typeof key]
);
}
}
);
} else {
// replace the control with a new one of the updated type
controlGroup.replacePanel(controlId, { panelType: newType, initialState: newState });
controlGroupApi.replacePanel(controlId, { panelType: newType, initialState: newState });
}
},
initialState: {
...initialState,
controlType,
controlId,
defaultPanelTitle: defaultPanelTitle.getValue(),
},
controlGroupApi: controlGroup,
controlType,
controlId,
initialDefaultPanelTitle: defaultPanelTitle.getValue(),
controlGroupApi,
});
};

View file

@ -20,23 +20,22 @@ import { ControlGroupApi } from '../../control_group/types';
import { DataControlEditor } from './data_control_editor';
import { DefaultDataControlState } from './types';
export type DataControlEditorState = Partial<DefaultDataControlState> & {
fieldName?: string;
controlType?: string;
controlId?: string;
defaultPanelTitle?: string;
};
export const openDataControlEditor = <
State extends DataControlEditorState = DataControlEditorState
State extends DefaultDataControlState = DefaultDataControlState
>({
initialState,
controlType,
controlId,
initialDefaultPanelTitle,
onSave,
controlGroupApi,
services,
}: {
initialState: State;
onSave: ({ type, state }: { type: string; state: State }) => void;
initialState: Partial<State>;
controlType?: string;
controlId?: string;
initialDefaultPanelTitle?: string;
onSave: ({ type, state }: { type: string; state: Partial<State> }) => void;
controlGroupApi: ControlGroupApi;
services: {
core: CoreStart;
@ -50,7 +49,7 @@ export const openDataControlEditor = <
overlayRef.close();
};
const onCancel = (newState: State, overlay: OverlayRef) => {
const onCancel = (newState: Partial<State>, overlay: OverlayRef) => {
if (deepEqual(initialState, newState)) {
closeOverlay(overlay);
return;
@ -83,8 +82,11 @@ export const openDataControlEditor = <
const overlay = services.core.overlays.openFlyout(
toMountPoint(
<DataControlEditor<State>
parentApi={controlGroupApi}
controlGroupApi={controlGroupApi}
initialState={initialState}
controlType={controlType}
controlId={controlId}
initialDefaultPanelTitle={initialDefaultPanelTitle}
onCancel={(state) => {
onCancel(state, overlay);
}}

View file

@ -34,9 +34,9 @@ describe('Options list sorting button', () => {
const mountComponent = ({
initialState,
field,
parentApi = getMockedControlGroupApi(),
controlGroupApi = getMockedControlGroupApi(),
}: Pick<CustomOptionsComponentProps, 'initialState' | 'field'> & {
parentApi?: ControlGroupApi;
controlGroupApi?: ControlGroupApi;
}) => {
const component = render(
<OptionsListEditorOptions
@ -44,7 +44,7 @@ describe('Options list sorting button', () => {
field={field}
updateState={updateState}
setControlEditorValid={jest.fn()}
parentApi={parentApi}
controlGroupApi={controlGroupApi}
/>
);
return component;
@ -85,7 +85,7 @@ describe('Options list sorting button', () => {
const component = mountComponent({
initialState: getMockedState(),
field: { type: 'string' } as DataViewField,
parentApi: controlGroupApi,
controlGroupApi,
});
expect(
component.queryByTestId('optionsListControl__searchOptionsRadioGroup')
@ -152,14 +152,14 @@ describe('Options list sorting button', () => {
describe('responds to field type changing', () => {
test('reset back to initial state when valid', async () => {
const initialState = getMockedState({ searchTechnique: 'exact' });
const parentApi = getMockedControlGroupApi();
const controlGroupApi = getMockedControlGroupApi();
const component = render(
<OptionsListEditorOptions
initialState={initialState}
field={{ type: 'string' } as DataViewField}
updateState={updateState}
setControlEditorValid={jest.fn()}
parentApi={parentApi}
controlGroupApi={controlGroupApi}
/>
);
@ -175,7 +175,7 @@ describe('Options list sorting button', () => {
field={{ type: 'ip' } as DataViewField} // initial search technique IS valid
updateState={jest.fn()}
setControlEditorValid={jest.fn()}
parentApi={parentApi}
controlGroupApi={controlGroupApi}
/>
);
@ -187,14 +187,14 @@ describe('Options list sorting button', () => {
test('if the current selection is valid, send that to the parent editor state', async () => {
const initialState = getMockedState();
const parentApi = getMockedControlGroupApi();
const controlGroupApi = getMockedControlGroupApi();
const component = render(
<OptionsListEditorOptions
initialState={initialState}
field={{ type: 'string' } as DataViewField}
updateState={updateState}
setControlEditorValid={jest.fn()}
parentApi={parentApi}
controlGroupApi={controlGroupApi}
/>
);
@ -218,7 +218,7 @@ describe('Options list sorting button', () => {
field={{ type: 'number' } as DataViewField} // current selected search technique IS valid, initial state is not
updateState={jest.fn()}
setControlEditorValid={jest.fn()}
parentApi={parentApi}
controlGroupApi={controlGroupApi}
/>
);
@ -227,14 +227,14 @@ describe('Options list sorting button', () => {
test('if neither the initial or current search technique is valid, revert to the default', async () => {
const initialState = getMockedState({ searchTechnique: 'wildcard' });
const parentApi = getMockedControlGroupApi();
const controlGroupApi = getMockedControlGroupApi();
const component = render(
<OptionsListEditorOptions
initialState={initialState}
field={{ type: 'string' } as DataViewField}
updateState={updateState}
setControlEditorValid={jest.fn()}
parentApi={parentApi}
controlGroupApi={controlGroupApi}
/>
);
@ -250,7 +250,7 @@ describe('Options list sorting button', () => {
field={{ type: 'number' } as DataViewField} // neither initial nor current search technique is valid
updateState={jest.fn()}
setControlEditorValid={jest.fn()}
parentApi={parentApi}
controlGroupApi={controlGroupApi}
/>
);

View file

@ -71,9 +71,11 @@ export const OptionsListEditorOptions = ({
initialState,
field,
updateState,
parentApi,
controlGroupApi,
}: CustomOptionsComponentProps<OptionsListControlState>) => {
const allowExpensiveQueries = useStateFromPublishingSubject(parentApi.allowExpensiveQueries$);
const allowExpensiveQueries = useStateFromPublishingSubject(
controlGroupApi.allowExpensiveQueries$
);
const [singleSelect, setSingleSelect] = useState<boolean>(initialState.singleSelect ?? false);
const [runPastTimeout, setRunPastTimeout] = useState<boolean>(

View file

@ -243,7 +243,7 @@ describe('RangesliderControlApi', () => {
field={{} as DataViewField}
updateState={jest.fn()}
setControlEditorValid={jest.fn()}
parentApi={controlGroupApi}
controlGroupApi={controlGroupApi}
/>
);
expect(
@ -260,7 +260,7 @@ describe('RangesliderControlApi', () => {
field={{} as DataViewField}
updateState={jest.fn()}
setControlEditorValid={setControlEditorValid}
parentApi={controlGroupApi}
controlGroupApi={controlGroupApi}
/>
);

View file

@ -38,11 +38,11 @@ export type DataControlApi = DefaultControlApi &
export interface CustomOptionsComponentProps<
State extends DefaultDataControlState = DefaultDataControlState
> {
initialState: Omit<State, 'fieldName'>;
initialState: Partial<State>;
field: DataViewField;
updateState: (newState: Partial<State>) => void;
setControlEditorValid: (valid: boolean) => void;
parentApi: ControlGroupApi;
controlGroupApi: ControlGroupApi;
}
export interface DataControlFactory<