Skip to content

Commit

Permalink
[Analyst Experience Components] Dashboard & Control Group APIs (#150121)
Browse files Browse the repository at this point in the history
Aligns the Portable Dashboard renderer and the Control Group renderer to a new API structure using `useImperativeHandle` rather than the overcomplicated and mostly unused wrapper provider system.
  • Loading branch information
ThomThomson authored Apr 19, 2023
1 parent befd429 commit ffc3492
Show file tree
Hide file tree
Showing 146 changed files with 2,768 additions and 2,850 deletions.
5 changes: 1 addition & 4 deletions examples/controls_example/public/add_button_example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,8 @@
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);
import { ControlGroupRenderer } from '@kbn/controls-plugin/public';

export const AddButtonExample = ({ dataViewId }: { dataViewId: string }) => {
return (
Expand Down
46 changes: 10 additions & 36 deletions examples/controls_example/public/basic_redux_example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,18 @@
* Side Public License, v 1.
*/

import React, { useMemo, useState } from 'react';
import React, { useState } from 'react';

import {
LazyControlGroupRenderer,
ControlGroupContainer,
useControlGroupContainerContext,
ControlStyle,
} from '@kbn/controls-plugin/public';
import { withSuspense } from '@kbn/presentation-util-plugin/public';
import { EuiButtonGroup, EuiPanel, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
import { ViewMode } from '@kbn/embeddable-plugin/public';

const ControlGroupRenderer = withSuspense(LazyControlGroupRenderer);
import { EuiButtonGroup, EuiPanel, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
import { ControlGroupRenderer, ControlStyle, ControlGroupAPI } from '@kbn/controls-plugin/public';
import { AwaitingControlGroupAPI } from '@kbn/controls-plugin/public/control_group';

export const BasicReduxExample = ({ dataViewId }: { dataViewId: string }) => {
const [controlGroup, setControlGroup] = useState<ControlGroupContainer>();

const ControlGroupReduxWrapper = useMemo(() => {
if (controlGroup) return controlGroup.getReduxEmbeddableTools().Wrapper;
}, [controlGroup]);

const ButtonControls = () => {
const {
useEmbeddableDispatch,
useEmbeddableSelector: select,
actions: { setControlStyle },
} = useControlGroupContainerContext();
const dispatch = useEmbeddableDispatch();
const controlStyle = select((state) => state.explicitInput.controlStyle);
const [controlGroupAPI, setControlGroupApi] = useState<AwaitingControlGroupAPI>(null);

const Buttons = ({ api }: { api: ControlGroupAPI }) => {
const controlStyle = api.select((state) => state.explicitInput.controlStyle);
return (
<EuiButtonGroup
legend="Text style"
Expand All @@ -52,9 +34,7 @@ export const BasicReduxExample = ({ dataViewId }: { dataViewId: string }) => {
},
]}
idSelected={controlStyle}
onChange={(id, value) => {
dispatch(setControlStyle(value));
}}
onChange={(id, value) => api.dispatch.setControlStyle(value)}
type="single"
/>
);
Expand All @@ -70,16 +50,10 @@ export const BasicReduxExample = ({ dataViewId }: { dataViewId: string }) => {
</EuiText>
<EuiSpacer size="m" />
<EuiPanel hasBorder={true}>
{ControlGroupReduxWrapper && (
<ControlGroupReduxWrapper>
<ButtonControls />
</ControlGroupReduxWrapper>
)}
{controlGroupAPI && <Buttons api={controlGroupAPI} />}

<ControlGroupRenderer
onLoadComplete={async (newControlGroup) => {
setControlGroup(newControlGroup);
}}
ref={setControlGroupApi}
getCreationOptions={async (initialInput, builder) => {
await builder.addDataControlFromField(initialInput, {
dataViewId,
Expand Down
30 changes: 13 additions & 17 deletions examples/controls_example/public/edit_example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,22 @@ import {
import { ViewMode } from '@kbn/embeddable-plugin/public';
import { OPTIONS_LIST_CONTROL, RANGE_SLIDER_CONTROL } from '@kbn/controls-plugin/common';
import {
LazyControlGroupRenderer,
ControlGroupContainer,
ControlGroupInput,
type ControlGroupInput,
ControlGroupRenderer,
AwaitingControlGroupAPI,
ACTION_EDIT_CONTROL,
ACTION_DELETE_CONTROL,
} from '@kbn/controls-plugin/public';
import { withSuspense } from '@kbn/presentation-util-plugin/public';
import { ControlInputTransform } from '@kbn/controls-plugin/common/types';

const ControlGroupRenderer = withSuspense(LazyControlGroupRenderer);

const INPUT_KEY = 'kbnControls:saveExample:input';

const WITH_CUSTOM_PLACEHOLDER = 'Custom Placeholder';

export const EditExample = () => {
const [isSaving, setIsSaving] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [controlGroup, setControlGroup] = useState<ControlGroupContainer>();
const [controlGroupAPI, setControlGroupAPI] = useState<AwaitingControlGroupAPI>(null);
const [toggleIconIdToSelectedMapIcon, setToggleIconIdToSelectedMapIcon] = useState<{
[id: string]: boolean;
}>({});
Expand All @@ -54,20 +51,21 @@ export const EditExample = () => {
},
};

if (controlGroup) {
if (controlGroupAPI) {
const disabledActions: string[] = Object.keys(
pickBy(newToggleIconIdToSelectedMapIcon, (value) => value)
);
controlGroup.updateInput({ disabledActions });
controlGroupAPI.updateInput({ disabledActions });
}

setToggleIconIdToSelectedMapIcon(newToggleIconIdToSelectedMapIcon);
}

async function onSave() {
setIsSaving(true);
if (!controlGroupAPI) return;

localStorage.setItem(INPUT_KEY, JSON.stringify(controlGroup!.getInput()));
setIsSaving(true);
localStorage.setItem(INPUT_KEY, JSON.stringify(controlGroupAPI.getInput()));

// simulated async save await
await new Promise((resolve) => setTimeout(resolve, 1000));
Expand Down Expand Up @@ -133,9 +131,9 @@ export const EditExample = () => {
<EuiButtonEmpty
color="primary"
iconType="plusInCircle"
isDisabled={controlGroup === undefined}
isDisabled={controlGroupAPI === undefined}
onClick={() => {
controlGroup!.openAddDataControlFlyout(controlInputTransform);
controlGroupAPI!.openAddDataControlFlyout(controlInputTransform);
}}
>
Add control
Expand Down Expand Up @@ -171,7 +169,7 @@ export const EditExample = () => {
<EuiButton
fill
color="primary"
isDisabled={controlGroup === undefined || isSaving}
isDisabled={controlGroupAPI === undefined || isSaving}
onClick={onSave}
isLoading={isSaving}
>
Expand All @@ -186,6 +184,7 @@ export const EditExample = () => {
</>
) : null}
<ControlGroupRenderer
ref={setControlGroupAPI}
getCreationOptions={async (initialInput, builder) => {
const persistedInput = await onLoad();
return {
Expand All @@ -196,9 +195,6 @@ export const EditExample = () => {
},
};
}}
onLoadComplete={async (newControlGroup) => {
setControlGroup(newControlGroup);
}}
/>
</EuiPanel>
</>
Expand Down
17 changes: 6 additions & 11 deletions examples/controls_example/public/search_example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,9 @@ import {
EuiText,
EuiTitle,
} from '@elastic/eui';
import { LazyControlGroupRenderer, ControlGroupContainer } from '@kbn/controls-plugin/public';
import { withSuspense } from '@kbn/presentation-util-plugin/public';
import { AwaitingControlGroupAPI, ControlGroupRenderer } from '@kbn/controls-plugin/public';
import { PLUGIN_ID } from './constants';

const ControlGroupRenderer = withSuspense(LazyControlGroupRenderer);

interface Props {
data: DataPublicPluginStart;
dataView: DataView;
Expand All @@ -36,7 +33,7 @@ interface Props {

export const SearchExample = ({ data, dataView, navigation }: Props) => {
const [controlFilters, setControlFilters] = useState<Filter[]>([]);
const [controlGroup, setControlGroup] = useState<ControlGroupContainer>();
const [controlGroupAPI, setControlGroupAPI] = useState<AwaitingControlGroupAPI>();
const [hits, setHits] = useState(0);
const [filters, setFilters] = useState<Filter[]>([]);
const [isSearching, setIsSearching] = useState(false);
Expand All @@ -47,16 +44,16 @@ export const SearchExample = ({ data, dataView, navigation }: Props) => {
const [timeRange, setTimeRange] = useState<TimeRange>({ from: 'now-7d', to: 'now' });

useEffect(() => {
if (!controlGroup) {
if (!controlGroupAPI) {
return;
}
const subscription = controlGroup.onFiltersPublished$.subscribe((newFilters) => {
const subscription = controlGroupAPI.onFiltersPublished$.subscribe((newFilters) => {
setControlFilters([...newFilters]);
});
return () => {
subscription.unsubscribe();
};
}, [controlGroup]);
}, [controlGroupAPI]);

useEffect(() => {
const abortController = new AbortController();
Expand Down Expand Up @@ -155,10 +152,8 @@ export const SearchExample = ({ data, dataView, navigation }: Props) => {
},
};
}}
onLoadComplete={async (newControlGroup) => {
setControlGroup(newControlGroup);
}}
query={query}
ref={setControlGroupAPI}
timeRange={timeRange}
/>
<EuiCallOut title="Search results">
Expand Down
3 changes: 1 addition & 2 deletions examples/controls_example/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,9 @@
"@kbn/data-plugin",
"@kbn/controls-plugin",
"@kbn/navigation-plugin",
"@kbn/presentation-util-plugin",
"@kbn/shared-ux-page-kibana-template",
"@kbn/embeddable-plugin",
"@kbn/data-views-plugin",
"@kbn/es-query"
"@kbn/es-query",
]
}
3 changes: 1 addition & 2 deletions examples/portable_dashboards_example/kibana.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
"unifiedSearch",
"developerExamples",
"embeddableExamples"
],
"requiredBundles": ["presentationUtil"]
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,37 @@
* Side Public License, v 1.
*/

import React from 'react';
import React, { useEffect, useState } from 'react';

import { withSuspense } from '@kbn/shared-ux-utility';
import { ViewMode } from '@kbn/embeddable-plugin/public';
import type { DataView } from '@kbn/data-views-plugin/public';
import { controlGroupInputBuilder } from '@kbn/controls-plugin/public';
import { EuiPanel, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
import { controlGroupInputBuilder } from '@kbn/controls-plugin/public';
import { getDefaultControlGroupInput } from '@kbn/controls-plugin/common';
import { FILTER_DEBUGGER_EMBEDDABLE } from '@kbn/embeddable-examples-plugin/public';
import { LazyDashboardContainerRenderer } from '@kbn/dashboard-plugin/public';

const DashboardContainerRenderer = withSuspense(LazyDashboardContainerRenderer);
import { AwaitingDashboardAPI, DashboardRenderer } from '@kbn/dashboard-plugin/public';

export const DashboardWithControlsExample = ({ dataView }: { dataView: DataView }) => {
const [dashboard, setDashboard] = useState<AwaitingDashboardAPI>();

// add a filter debugger panel as soon as the dashboard becomes available
useEffect(() => {
if (!dashboard) return;
(async () => {
const embeddable = await dashboard.addNewEmbeddable(FILTER_DEBUGGER_EMBEDDABLE, {});
const prevPanelState = dashboard.getExplicitInput().panels[embeddable.id];
// resize the new panel so that it fills up the entire width of the dashboard
dashboard.updateInput({
panels: {
[embeddable.id]: {
...prevPanelState,
gridData: { i: embeddable.id, x: 0, y: 0, w: 48, h: 12 },
},
},
});
})();
}, [dashboard]);

return (
<>
<EuiTitle>
Expand All @@ -29,10 +47,10 @@ export const DashboardWithControlsExample = ({ dataView }: { dataView: DataView
</EuiText>
<EuiSpacer size="m" />
<EuiPanel hasBorder={true}>
<DashboardContainerRenderer
<DashboardRenderer
getCreationOptions={async () => {
const builder = controlGroupInputBuilder;
const controlGroupInput = {};
const controlGroupInput = getDefaultControlGroupInput();
await builder.addDataControlFromField(controlGroupInput, {
dataViewId: dataView.id ?? '',
title: 'Destintion country',
Expand All @@ -57,22 +75,7 @@ export const DashboardWithControlsExample = ({ dataView }: { dataView: DataView
},
};
}}
onDashboardContainerLoaded={(container) => {
const addFilterEmbeddable = async () => {
const embeddable = await container.addNewEmbeddable(FILTER_DEBUGGER_EMBEDDABLE, {});
const prevPanelState = container.getExplicitInput().panels[embeddable.id];
// resize the new panel so that it fills up the entire width of the dashboard
container.updateInput({
panels: {
[embeddable.id]: {
...prevPanelState,
gridData: { i: embeddable.id, x: 0, y: 0, w: 48, h: 12 },
},
},
});
};
addFilterEmbeddable();
}}
ref={setDashboard}
/>
</EuiPanel>
</>
Expand Down
Loading

0 comments on commit ffc3492

Please sign in to comment.