mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 18:51:07 -04:00
[Analyst Experience Components] Dashboard & Control Group APIs (#150121)
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.
This commit is contained in:
parent
befd429f4f
commit
ffc349225e
146 changed files with 2772 additions and 2854 deletions
|
@ -20,7 +20,6 @@
|
|||
"unifiedSearch",
|
||||
"developerExamples",
|
||||
"embeddableExamples"
|
||||
],
|
||||
"requiredBundles": ["presentationUtil"]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
@ -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',
|
||||
|
@ -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>
|
||||
</>
|
||||
|
|
|
@ -6,9 +6,8 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { DashboardContainer, LazyDashboardContainerRenderer } from '@kbn/dashboard-plugin/public';
|
||||
import {
|
||||
EuiButtonGroup,
|
||||
EuiFlexGroup,
|
||||
|
@ -18,35 +17,19 @@ import {
|
|||
EuiText,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import {
|
||||
AwaitingDashboardAPI,
|
||||
DashboardAPI,
|
||||
DashboardRenderer,
|
||||
} from '@kbn/dashboard-plugin/public';
|
||||
import { ViewMode } from '@kbn/embeddable-plugin/public';
|
||||
import { withSuspense } from '@kbn/presentation-util-plugin/public';
|
||||
import { useDashboardContainerContext } from '@kbn/dashboard-plugin/public';
|
||||
|
||||
const DashboardContainerRenderer = withSuspense(LazyDashboardContainerRenderer);
|
||||
|
||||
export const DualReduxExample = () => {
|
||||
const [firstDashboardContainer, setFirstDashboardContainer] = useState<
|
||||
DashboardContainer | undefined
|
||||
>();
|
||||
const [secondDashboardContainer, setSecondDashboardContainer] = useState<
|
||||
DashboardContainer | undefined
|
||||
>();
|
||||
const [firstDashboardContainer, setFirstDashboardContainer] = useState<AwaitingDashboardAPI>();
|
||||
const [secondDashboardContainer, setSecondDashboardContainer] = useState<AwaitingDashboardAPI>();
|
||||
|
||||
const FirstDashboardReduxWrapper = useMemo(() => {
|
||||
if (firstDashboardContainer) return firstDashboardContainer.getReduxEmbeddableTools().Wrapper;
|
||||
}, [firstDashboardContainer]);
|
||||
const SecondDashboardReduxWrapper = useMemo(() => {
|
||||
if (secondDashboardContainer) return secondDashboardContainer.getReduxEmbeddableTools().Wrapper;
|
||||
}, [secondDashboardContainer]);
|
||||
|
||||
const ButtonControls = () => {
|
||||
const {
|
||||
useEmbeddableDispatch,
|
||||
useEmbeddableSelector: select,
|
||||
actions: { setViewMode },
|
||||
} = useDashboardContainerContext();
|
||||
const dispatch = useEmbeddableDispatch();
|
||||
const viewMode = select((state) => state.explicitInput.viewMode);
|
||||
const ButtonControls = ({ dashboard }: { dashboard: DashboardAPI }) => {
|
||||
const viewMode = dashboard.select((state) => state.explicitInput.viewMode);
|
||||
|
||||
return (
|
||||
<EuiButtonGroup
|
||||
|
@ -64,9 +47,7 @@ export const DualReduxExample = () => {
|
|||
},
|
||||
]}
|
||||
idSelected={viewMode}
|
||||
onChange={(id, value) => {
|
||||
dispatch(setViewMode(value));
|
||||
}}
|
||||
onChange={(id, value) => dashboard.dispatch.setViewMode(value)}
|
||||
type="single"
|
||||
/>
|
||||
);
|
||||
|
@ -91,34 +72,18 @@ export const DualReduxExample = () => {
|
|||
<h3>Dashboard #1</h3>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="m" />
|
||||
{FirstDashboardReduxWrapper && (
|
||||
<FirstDashboardReduxWrapper>
|
||||
<ButtonControls />
|
||||
</FirstDashboardReduxWrapper>
|
||||
)}
|
||||
{firstDashboardContainer && <ButtonControls dashboard={firstDashboardContainer} />}
|
||||
<EuiSpacer size="m" />
|
||||
<DashboardContainerRenderer
|
||||
onDashboardContainerLoaded={(container) => {
|
||||
setFirstDashboardContainer(container);
|
||||
}}
|
||||
/>
|
||||
<DashboardRenderer ref={setFirstDashboardContainer} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiTitle size="xs">
|
||||
<h3>Dashboard #2</h3>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="m" />
|
||||
{SecondDashboardReduxWrapper && (
|
||||
<SecondDashboardReduxWrapper>
|
||||
<ButtonControls />
|
||||
</SecondDashboardReduxWrapper>
|
||||
)}
|
||||
{secondDashboardContainer && <ButtonControls dashboard={secondDashboardContainer} />}
|
||||
<EuiSpacer size="m" />
|
||||
<DashboardContainerRenderer
|
||||
onDashboardContainerLoaded={(container) => {
|
||||
setSecondDashboardContainer(container);
|
||||
}}
|
||||
/>
|
||||
<DashboardRenderer ref={setSecondDashboardContainer} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPanel>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import React, { useMemo, useState } from 'react';
|
||||
|
||||
import { DashboardContainer, LazyDashboardContainerRenderer } from '@kbn/dashboard-plugin/public';
|
||||
import { AwaitingDashboardAPI, DashboardRenderer } from '@kbn/dashboard-plugin/public';
|
||||
import {
|
||||
EuiButton,
|
||||
EuiFlexGroup,
|
||||
|
@ -23,19 +23,17 @@ import {
|
|||
VisualizeInput,
|
||||
VisualizeOutput,
|
||||
} from '@kbn/visualizations-plugin/public/embeddable/visualize_embeddable';
|
||||
import { withSuspense } from '@kbn/presentation-util-plugin/public';
|
||||
|
||||
const INPUT_KEY = 'portableDashboard:saveExample:input';
|
||||
|
||||
const DashboardContainerRenderer = withSuspense(LazyDashboardContainerRenderer); // make this so we don't have two loading states - loading in the dashboard plugin instead
|
||||
|
||||
export const DynamicByReferenceExample = () => {
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const [dashboardContainer, setDashboardContainer] = useState<DashboardContainer | undefined>();
|
||||
const [dashboard, setdashboard] = useState<AwaitingDashboardAPI>();
|
||||
|
||||
const onSave = async () => {
|
||||
if (!dashboard) return;
|
||||
setIsSaving(true);
|
||||
localStorage.setItem(INPUT_KEY, JSON.stringify(dashboardContainer!.getInput()));
|
||||
localStorage.setItem(INPUT_KEY, JSON.stringify(dashboard.getInput()));
|
||||
// simulated async save await
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
setIsSaving(false);
|
||||
|
@ -56,44 +54,37 @@ export const DynamicByReferenceExample = () => {
|
|||
|
||||
const resetPersistableInput = () => {
|
||||
localStorage.removeItem(INPUT_KEY);
|
||||
if (dashboardContainer) {
|
||||
const children = dashboardContainer.getChildIds();
|
||||
if (dashboard) {
|
||||
const children = dashboard.getChildIds();
|
||||
children.map((childId) => {
|
||||
dashboardContainer.removeEmbeddable(childId);
|
||||
dashboard.removeEmbeddable(childId);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const addByReference = () => {
|
||||
if (dashboardContainer) {
|
||||
dashboardContainer.addFromLibrary();
|
||||
}
|
||||
};
|
||||
|
||||
const addByValue = async () => {
|
||||
if (dashboardContainer) {
|
||||
dashboardContainer.addNewEmbeddable<VisualizeInput, VisualizeOutput, VisualizeEmbeddable>(
|
||||
'visualization',
|
||||
{
|
||||
title: 'Sample Markdown Vis',
|
||||
savedVis: {
|
||||
type: 'markdown',
|
||||
title: '',
|
||||
data: { aggs: [], searchSource: {} },
|
||||
params: {
|
||||
fontSize: 12,
|
||||
openLinksInNewTab: false,
|
||||
markdown: '### By Value Visualization\nThis is a sample by value panel.',
|
||||
},
|
||||
if (!dashboard) return;
|
||||
dashboard.addNewEmbeddable<VisualizeInput, VisualizeOutput, VisualizeEmbeddable>(
|
||||
'visualization',
|
||||
{
|
||||
title: 'Sample Markdown Vis',
|
||||
savedVis: {
|
||||
type: 'markdown',
|
||||
title: '',
|
||||
data: { aggs: [], searchSource: {} },
|
||||
params: {
|
||||
fontSize: 12,
|
||||
openLinksInNewTab: false,
|
||||
markdown: '### By Value Visualization\nThis is a sample by value panel.',
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const disableButtons = useMemo(() => {
|
||||
return dashboardContainer === undefined || isSaving;
|
||||
}, [dashboardContainer, isSaving]);
|
||||
return !dashboard || isSaving;
|
||||
}, [dashboard, isSaving]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -114,7 +105,7 @@ export const DynamicByReferenceExample = () => {
|
|||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiButton onClick={addByReference} isDisabled={disableButtons}>
|
||||
<EuiButton onClick={() => dashboard?.addFromLibrary()} isDisabled={disableButtons}>
|
||||
Add visualization from library
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
|
@ -141,7 +132,7 @@ export const DynamicByReferenceExample = () => {
|
|||
</EuiFlexGroup>
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
<DashboardContainerRenderer
|
||||
<DashboardRenderer
|
||||
getCreationOptions={async () => {
|
||||
const persistedInput = getPersistableInput();
|
||||
return {
|
||||
|
@ -151,9 +142,7 @@ export const DynamicByReferenceExample = () => {
|
|||
},
|
||||
};
|
||||
}}
|
||||
onDashboardContainerLoaded={(container) => {
|
||||
setDashboardContainer(container);
|
||||
}}
|
||||
ref={setdashboard}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</>
|
||||
|
|
|
@ -11,15 +11,9 @@ import { css } from '@emotion/react';
|
|||
|
||||
import { buildPhraseFilter, Filter } from '@kbn/es-query';
|
||||
import type { DataView } from '@kbn/data-views-plugin/public';
|
||||
import {
|
||||
LazyDashboardContainerRenderer,
|
||||
DashboardCreationOptions,
|
||||
} from '@kbn/dashboard-plugin/public';
|
||||
import { DashboardRenderer, DashboardCreationOptions } from '@kbn/dashboard-plugin/public';
|
||||
import { EuiCode, EuiPanel, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
|
||||
import { ViewMode } from '@kbn/embeddable-plugin/public';
|
||||
import { withSuspense } from '@kbn/presentation-util-plugin/public';
|
||||
|
||||
const DashboardContainerRenderer = withSuspense(LazyDashboardContainerRenderer);
|
||||
|
||||
export const StaticByReferenceExample = ({
|
||||
dashboardId,
|
||||
|
@ -50,7 +44,7 @@ export const StaticByReferenceExample = ({
|
|||
overflow-y: auto;
|
||||
`}
|
||||
>
|
||||
<DashboardContainerRenderer
|
||||
<DashboardRenderer
|
||||
savedObjectId={dashboardId}
|
||||
getCreationOptions={async () => {
|
||||
const field = dataView.getFieldByName('machine.os.keyword');
|
||||
|
@ -61,13 +55,10 @@ export const StaticByReferenceExample = ({
|
|||
if (field) {
|
||||
filter = buildPhraseFilter(field, 'win xp', dataView);
|
||||
filter.meta.negate = true;
|
||||
creationOptions = { ...creationOptions, overrideInput: { filters: [filter] } };
|
||||
creationOptions = { ...creationOptions, initialInput: { filters: [filter] } };
|
||||
}
|
||||
return creationOptions; // if can't find the field, then just return no special creation options
|
||||
}}
|
||||
onDashboardContainerLoaded={(container) => {
|
||||
return; // this example is static, so don't need to do anything with the dashboard container
|
||||
}}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</>
|
||||
|
|
|
@ -11,13 +11,10 @@ import React from 'react';
|
|||
import { ViewMode } from '@kbn/embeddable-plugin/public';
|
||||
import { EuiPanel, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
|
||||
import type { DashboardPanelMap } from '@kbn/dashboard-plugin/common';
|
||||
import { LazyDashboardContainerRenderer } from '@kbn/dashboard-plugin/public';
|
||||
import { withSuspense } from '@kbn/presentation-util-plugin/public';
|
||||
import { DashboardRenderer } from '@kbn/dashboard-plugin/public';
|
||||
|
||||
import panelsJson from './static_by_value_example_panels.json';
|
||||
|
||||
const DashboardContainerRenderer = withSuspense(LazyDashboardContainerRenderer);
|
||||
|
||||
export const StaticByValueExample = () => {
|
||||
return (
|
||||
<>
|
||||
|
@ -29,7 +26,7 @@ export const StaticByValueExample = () => {
|
|||
</EuiText>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiPanel hasBorder={true}>
|
||||
<DashboardContainerRenderer
|
||||
<DashboardRenderer
|
||||
getCreationOptions={async () => {
|
||||
return {
|
||||
initialInput: {
|
||||
|
@ -39,9 +36,6 @@ export const StaticByValueExample = () => {
|
|||
},
|
||||
};
|
||||
}}
|
||||
onDashboardContainerLoaded={(container) => {
|
||||
return; // this example is static, so don't need to do anything with the dashboard container
|
||||
}}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</>
|
||||
|
|
|
@ -20,11 +20,9 @@
|
|||
"@kbn/embeddable-plugin",
|
||||
"@kbn/data-views-plugin",
|
||||
"@kbn/visualizations-plugin",
|
||||
"@kbn/presentation-util-plugin",
|
||||
"@kbn/developer-examples-plugin",
|
||||
"@kbn/embeddable-examples-plugin",
|
||||
"@kbn/shared-ux-page-kibana-template",
|
||||
"@kbn/shared-ux-utility",
|
||||
"@kbn/controls-plugin",
|
||||
"@kbn/shared-ux-router"
|
||||
]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue