mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[dashboard] Migrate Dashboard internal components to DashboardApi (#193220)
PR replaces `useDashboardContainer` with `useDashboardApi`. `useDashboardApi` returns `DashboardApi` instead of `DashboardContainer`. After this PR, all react context's in dashboard return `DashboardApi` and thus all components are now prepared for the migration from DashboardContainer to DashboardApi. --------- Co-authored-by: Hannah Mudge <Heenawter@users.noreply.github.com> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
3fa5bdf873
commit
92da1767f3
15 changed files with 299 additions and 181 deletions
|
@ -9,13 +9,17 @@
|
|||
|
||||
import {
|
||||
CanExpandPanels,
|
||||
HasRuntimeChildState,
|
||||
HasSerializedChildState,
|
||||
PresentationContainer,
|
||||
SerializedPanelState,
|
||||
TracksOverlays,
|
||||
} from '@kbn/presentation-containers';
|
||||
import {
|
||||
HasAppContext,
|
||||
HasType,
|
||||
PublishesDataViews,
|
||||
PublishesPanelDescription,
|
||||
PublishesPanelTitle,
|
||||
PublishesSavedObjectId,
|
||||
PublishesUnifiedSearch,
|
||||
|
@ -23,41 +27,64 @@ import {
|
|||
PublishingSubject,
|
||||
ViewMode,
|
||||
} from '@kbn/presentation-publishing';
|
||||
import { ControlGroupApi } from '@kbn/controls-plugin/public';
|
||||
import { ControlGroupApi, ControlGroupSerializedState } from '@kbn/controls-plugin/public';
|
||||
import { Filter, Query, TimeRange } from '@kbn/es-query';
|
||||
import { DefaultEmbeddableApi, ErrorEmbeddable, IEmbeddable } from '@kbn/embeddable-plugin/public';
|
||||
import { DashboardPanelMap, DashboardPanelState } from '../../common';
|
||||
import { SaveDashboardReturn } from '../services/dashboard_content_management/types';
|
||||
import { DashboardStateFromSettingsFlyout, UnsavedPanelState } from '../dashboard_container/types';
|
||||
|
||||
export type DashboardApi = CanExpandPanels &
|
||||
HasAppContext &
|
||||
HasRuntimeChildState &
|
||||
HasSerializedChildState &
|
||||
HasType<'dashboard'> &
|
||||
PresentationContainer &
|
||||
PublishesDataViews &
|
||||
PublishesPanelDescription &
|
||||
Pick<PublishesPanelTitle, 'panelTitle'> &
|
||||
PublishesSavedObjectId &
|
||||
PublishesUnifiedSearch &
|
||||
PublishesViewMode &
|
||||
TracksOverlays & {
|
||||
addFromLibrary: () => void;
|
||||
animatePanelTransforms$: PublishingSubject<boolean | undefined>;
|
||||
asyncResetToLastSavedState: () => Promise<void>;
|
||||
controlGroupApi$: PublishingSubject<ControlGroupApi | undefined>;
|
||||
embeddedExternally$: PublishingSubject<boolean | undefined>;
|
||||
fullScreenMode$: PublishingSubject<boolean | undefined>;
|
||||
focusedPanelId$: PublishingSubject<string | undefined>;
|
||||
forceRefresh: () => void;
|
||||
getRuntimeStateForControlGroup: () => UnsavedPanelState | undefined;
|
||||
getSerializedStateForControlGroup: () => SerializedPanelState<ControlGroupSerializedState>;
|
||||
getSettings: () => DashboardStateFromSettingsFlyout;
|
||||
getDashboardPanelFromId: (id: string) => Promise<DashboardPanelState>;
|
||||
getPanelsState: () => DashboardPanelMap;
|
||||
hasOverlays$: PublishingSubject<boolean | undefined>;
|
||||
hasRunMigrations$: PublishingSubject<boolean | undefined>;
|
||||
hasUnsavedChanges$: PublishingSubject<boolean | undefined>;
|
||||
highlightPanel: (panelRef: HTMLDivElement) => void;
|
||||
highlightPanelId$: PublishingSubject<string | undefined>;
|
||||
managed$: PublishingSubject<boolean | undefined>;
|
||||
panels$: PublishingSubject<DashboardPanelMap>;
|
||||
registerChildApi: (api: DefaultEmbeddableApi) => void;
|
||||
runInteractiveSave: (interactionMode: ViewMode) => Promise<SaveDashboardReturn | undefined>;
|
||||
runQuickSave: () => Promise<void>;
|
||||
scrollToPanel: (panelRef: HTMLDivElement) => void;
|
||||
scrollToPanelId$: PublishingSubject<string | undefined>;
|
||||
scrollToTop: () => void;
|
||||
setControlGroupApi: (controlGroupApi: ControlGroupApi) => void;
|
||||
setSettings: (settings: DashboardStateFromSettingsFlyout) => void;
|
||||
setFilters: (filters?: Filter[] | undefined) => void;
|
||||
setFullScreenMode: (fullScreenMode: boolean) => void;
|
||||
setPanels: (panels: DashboardPanelMap) => void;
|
||||
setQuery: (query?: Query | undefined) => void;
|
||||
setTags: (tags: string[]) => void;
|
||||
setTimeRange: (timeRange?: TimeRange | undefined) => void;
|
||||
setViewMode: (viewMode: ViewMode) => void;
|
||||
openSettingsFlyout: () => void;
|
||||
useMargins$: PublishingSubject<boolean | undefined>;
|
||||
// TODO replace with HasUniqueId once dashboard is refactored and navigateToDashboard is removed
|
||||
uuid$: PublishingSubject<string>;
|
||||
|
||||
// TODO remove types below this line - from legacy embeddable system
|
||||
untilEmbeddableLoaded: (id: string) => Promise<IEmbeddable | ErrorEmbeddable>;
|
||||
};
|
||||
|
|
|
@ -23,6 +23,7 @@ import { CHANGE_CHECK_DEBOUNCE } from '../../dashboard_constants';
|
|||
import { confirmDiscardUnsavedChanges } from '../../dashboard_listing/confirm_overlays';
|
||||
import { SaveDashboardReturn } from '../../services/dashboard_content_management/types';
|
||||
import { useDashboardApi } from '../../dashboard_api/use_dashboard_api';
|
||||
import { openSettingsFlyout } from '../../dashboard_container/embeddable/api';
|
||||
|
||||
export const useDashboardMenuItems = ({
|
||||
isLabsShown,
|
||||
|
@ -84,7 +85,7 @@ export const useDashboardMenuItems = ({
|
|||
anchorElement,
|
||||
savedObjectId: lastSavedId,
|
||||
isDirty: Boolean(hasUnsavedChanges),
|
||||
getPanelsState: dashboardApi.getPanelsState,
|
||||
getPanelsState: () => dashboardApi.panels$.value,
|
||||
});
|
||||
},
|
||||
[dashboardTitle, hasUnsavedChanges, lastSavedId, dashboardApi]
|
||||
|
@ -227,7 +228,7 @@ export const useDashboardMenuItems = ({
|
|||
id: 'settings',
|
||||
testId: 'dashboardSettingsButton',
|
||||
disableButton: disableTopNav,
|
||||
run: () => dashboardApi.openSettingsFlyout(),
|
||||
run: () => openSettingsFlyout(dashboardApi),
|
||||
},
|
||||
};
|
||||
}, [
|
||||
|
|
|
@ -14,7 +14,8 @@ import { findTestSubject } from '@elastic/eui/lib/test';
|
|||
import { buildMockDashboard } from '../../../mocks';
|
||||
import { DashboardEmptyScreen } from './dashboard_empty_screen';
|
||||
import { pluginServices } from '../../../services/plugin_services';
|
||||
import { DashboardContainerContext } from '../../embeddable/dashboard_container';
|
||||
import { DashboardContext } from '../../../dashboard_api/use_dashboard_api';
|
||||
import { DashboardApi } from '../../../dashboard_api/types';
|
||||
import { ViewMode } from '@kbn/embeddable-plugin/public';
|
||||
|
||||
pluginServices.getServices().visualizations.getAliases = jest
|
||||
|
@ -23,11 +24,11 @@ pluginServices.getServices().visualizations.getAliases = jest
|
|||
|
||||
describe('DashboardEmptyScreen', () => {
|
||||
function mountComponent(viewMode: ViewMode) {
|
||||
const dashboardContainer = buildMockDashboard({ overrides: { viewMode } });
|
||||
const dashboardApi = buildMockDashboard({ overrides: { viewMode } }) as DashboardApi;
|
||||
return mountWithIntl(
|
||||
<DashboardContainerContext.Provider value={dashboardContainer}>
|
||||
<DashboardContext.Provider value={dashboardApi}>
|
||||
<DashboardEmptyScreen />
|
||||
</DashboardContainerContext.Provider>
|
||||
</DashboardContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,9 +22,10 @@ import {
|
|||
import { METRIC_TYPE } from '@kbn/analytics';
|
||||
import { ViewMode } from '@kbn/embeddable-plugin/public';
|
||||
|
||||
import { useStateFromPublishingSubject } from '@kbn/presentation-publishing';
|
||||
import { DASHBOARD_UI_METRIC_ID } from '../../../dashboard_constants';
|
||||
import { pluginServices } from '../../../services/plugin_services';
|
||||
import { useDashboardContainer } from '../../embeddable/dashboard_container';
|
||||
import { useDashboardApi } from '../../../dashboard_api/use_dashboard_api';
|
||||
import { emptyScreenStrings } from '../../_dashboard_container_strings';
|
||||
|
||||
export function DashboardEmptyScreen() {
|
||||
|
@ -45,13 +46,19 @@ export function DashboardEmptyScreen() {
|
|||
[getVisTypeAliases]
|
||||
);
|
||||
|
||||
const dashboardContainer = useDashboardContainer();
|
||||
const dashboardApi = useDashboardApi();
|
||||
const isDarkTheme = useObservable(theme$)?.darkMode;
|
||||
const isEditMode =
|
||||
dashboardContainer.select((state) => state.explicitInput.viewMode) === ViewMode.EDIT;
|
||||
const embeddableAppContext = dashboardContainer.getAppContext();
|
||||
const originatingPath = embeddableAppContext?.getCurrentPath?.() ?? '';
|
||||
const originatingApp = embeddableAppContext?.currentAppId;
|
||||
const viewMode = useStateFromPublishingSubject(dashboardApi.viewMode);
|
||||
const isEditMode = useMemo(() => {
|
||||
return viewMode === 'edit';
|
||||
}, [viewMode]);
|
||||
const { originatingPath, originatingApp } = useMemo(() => {
|
||||
const appContext = dashboardApi.getAppContext();
|
||||
return {
|
||||
originatingApp: appContext?.currentAppId,
|
||||
originatingPath: appContext?.getCurrentPath?.() ?? '',
|
||||
};
|
||||
}, [dashboardApi]);
|
||||
|
||||
const goToLens = useCallback(() => {
|
||||
if (!lensAlias || !lensAlias.alias) return;
|
||||
|
@ -128,7 +135,7 @@ export function DashboardEmptyScreen() {
|
|||
<EuiButtonEmpty
|
||||
flush="left"
|
||||
iconType="folderOpen"
|
||||
onClick={() => dashboardContainer.addFromLibrary()}
|
||||
onClick={() => dashboardApi.addFromLibrary()}
|
||||
>
|
||||
{emptyScreenStrings.getAddFromLibraryButtonTitle()}
|
||||
</EuiButtonEmpty>
|
||||
|
@ -138,10 +145,7 @@ export function DashboardEmptyScreen() {
|
|||
}
|
||||
if (showWriteControls) {
|
||||
return (
|
||||
<EuiButton
|
||||
iconType="pencil"
|
||||
onClick={() => dashboardContainer.dispatch.setViewMode(ViewMode.EDIT)}
|
||||
>
|
||||
<EuiButton iconType="pencil" onClick={() => dashboardApi.setViewMode(ViewMode.EDIT)}>
|
||||
{emptyScreenStrings.getEditLinkTitle()}
|
||||
</EuiButton>
|
||||
);
|
||||
|
|
|
@ -15,7 +15,9 @@ import { CONTACT_CARD_EMBEDDABLE } from '@kbn/embeddable-plugin/public/lib/test_
|
|||
import { DashboardGrid } from './dashboard_grid';
|
||||
import { buildMockDashboard } from '../../../mocks';
|
||||
import type { Props as DashboardGridItemProps } from './dashboard_grid_item';
|
||||
import { DashboardContainerContext } from '../../embeddable/dashboard_container';
|
||||
import { DashboardContext } from '../../../dashboard_api/use_dashboard_api';
|
||||
import { DashboardApi } from '../../../dashboard_api/types';
|
||||
import { DashboardPanelMap } from '../../../../common';
|
||||
|
||||
jest.mock('./dashboard_grid_item', () => {
|
||||
return {
|
||||
|
@ -45,59 +47,62 @@ jest.mock('./dashboard_grid_item', () => {
|
|||
};
|
||||
});
|
||||
|
||||
const createAndMountDashboardGrid = async () => {
|
||||
const PANELS = {
|
||||
'1': {
|
||||
gridData: { x: 0, y: 0, w: 6, h: 6, i: '1' },
|
||||
type: CONTACT_CARD_EMBEDDABLE,
|
||||
explicitInput: { id: '1' },
|
||||
},
|
||||
'2': {
|
||||
gridData: { x: 6, y: 6, w: 6, h: 6, i: '2' },
|
||||
type: CONTACT_CARD_EMBEDDABLE,
|
||||
explicitInput: { id: '2' },
|
||||
},
|
||||
};
|
||||
|
||||
const createAndMountDashboardGrid = async (panels: DashboardPanelMap = PANELS) => {
|
||||
const dashboardContainer = buildMockDashboard({
|
||||
overrides: {
|
||||
panels: {
|
||||
'1': {
|
||||
gridData: { x: 0, y: 0, w: 6, h: 6, i: '1' },
|
||||
type: CONTACT_CARD_EMBEDDABLE,
|
||||
explicitInput: { id: '1' },
|
||||
},
|
||||
'2': {
|
||||
gridData: { x: 6, y: 6, w: 6, h: 6, i: '2' },
|
||||
type: CONTACT_CARD_EMBEDDABLE,
|
||||
explicitInput: { id: '2' },
|
||||
},
|
||||
},
|
||||
panels,
|
||||
},
|
||||
});
|
||||
await dashboardContainer.untilContainerInitialized();
|
||||
const component = mountWithIntl(
|
||||
<DashboardContainerContext.Provider value={dashboardContainer}>
|
||||
<DashboardContext.Provider value={dashboardContainer as DashboardApi}>
|
||||
<DashboardGrid viewportWidth={1000} />
|
||||
</DashboardContainerContext.Provider>
|
||||
</DashboardContext.Provider>
|
||||
);
|
||||
return { dashboardContainer, component };
|
||||
return { dashboardApi: dashboardContainer, component };
|
||||
};
|
||||
|
||||
test('renders DashboardGrid', async () => {
|
||||
const { component } = await createAndMountDashboardGrid();
|
||||
const { component } = await createAndMountDashboardGrid(PANELS);
|
||||
const panelElements = component.find('GridItem');
|
||||
expect(panelElements.length).toBe(2);
|
||||
});
|
||||
|
||||
test('renders DashboardGrid with no visualizations', async () => {
|
||||
const { dashboardContainer, component } = await createAndMountDashboardGrid();
|
||||
dashboardContainer.updateInput({ panels: {} });
|
||||
component.update();
|
||||
const { component } = await createAndMountDashboardGrid({});
|
||||
expect(component.find('GridItem').length).toBe(0);
|
||||
});
|
||||
|
||||
test('DashboardGrid removes panel when removed from container', async () => {
|
||||
const { dashboardContainer, component } = await createAndMountDashboardGrid();
|
||||
const originalPanels = dashboardContainer.getInput().panels;
|
||||
const filteredPanels = { ...originalPanels };
|
||||
delete filteredPanels['1'];
|
||||
dashboardContainer.updateInput({ panels: filteredPanels });
|
||||
const { dashboardApi, component } = await createAndMountDashboardGrid(PANELS);
|
||||
expect(component.find('GridItem').length).toBe(2);
|
||||
|
||||
dashboardApi.setPanels({
|
||||
'2': PANELS['2'],
|
||||
});
|
||||
await new Promise((resolve) => setTimeout(resolve, 1));
|
||||
component.update();
|
||||
const panelElements = component.find('GridItem');
|
||||
expect(panelElements.length).toBe(1);
|
||||
|
||||
expect(component.find('GridItem').length).toBe(1);
|
||||
});
|
||||
|
||||
test('DashboardGrid renders expanded panel', async () => {
|
||||
const { dashboardContainer, component } = await createAndMountDashboardGrid();
|
||||
dashboardContainer.setExpandedPanelId('1');
|
||||
const { dashboardApi, component } = await createAndMountDashboardGrid();
|
||||
dashboardApi.setExpandedPanelId('1');
|
||||
await new Promise((resolve) => setTimeout(resolve, 1));
|
||||
component.update();
|
||||
// Both panels should still exist in the dom, so nothing needs to be re-fetched once minimized.
|
||||
expect(component.find('GridItem').length).toBe(2);
|
||||
|
@ -105,7 +110,8 @@ test('DashboardGrid renders expanded panel', async () => {
|
|||
expect(component.find('#mockDashboardGridItem_1').hasClass('expandedPanel')).toBe(true);
|
||||
expect(component.find('#mockDashboardGridItem_2').hasClass('hiddenPanel')).toBe(true);
|
||||
|
||||
dashboardContainer.setExpandedPanelId();
|
||||
dashboardApi.setExpandedPanelId();
|
||||
await new Promise((resolve) => setTimeout(resolve, 1));
|
||||
component.update();
|
||||
expect(component.find('GridItem').length).toBe(2);
|
||||
|
||||
|
@ -114,8 +120,9 @@ test('DashboardGrid renders expanded panel', async () => {
|
|||
});
|
||||
|
||||
test('DashboardGrid renders focused panel', async () => {
|
||||
const { dashboardContainer, component } = await createAndMountDashboardGrid();
|
||||
dashboardContainer.setFocusedPanelId('2');
|
||||
const { dashboardApi, component } = await createAndMountDashboardGrid();
|
||||
dashboardApi.setFocusedPanelId('2');
|
||||
await new Promise((resolve) => setTimeout(resolve, 1));
|
||||
component.update();
|
||||
// Both panels should still exist in the dom, so nothing needs to be re-fetched once minimized.
|
||||
expect(component.find('GridItem').length).toBe(2);
|
||||
|
@ -123,7 +130,8 @@ test('DashboardGrid renders focused panel', async () => {
|
|||
expect(component.find('#mockDashboardGridItem_1').hasClass('blurredPanel')).toBe(true);
|
||||
expect(component.find('#mockDashboardGridItem_2').hasClass('focusedPanel')).toBe(true);
|
||||
|
||||
dashboardContainer.setFocusedPanelId(undefined);
|
||||
dashboardApi.setFocusedPanelId(undefined);
|
||||
await new Promise((resolve) => setTimeout(resolve, 1));
|
||||
component.update();
|
||||
expect(component.find('GridItem').length).toBe(2);
|
||||
|
||||
|
|
|
@ -17,23 +17,26 @@ import { Layout, Responsive as ResponsiveReactGridLayout } from 'react-grid-layo
|
|||
|
||||
import { ViewMode } from '@kbn/embeddable-plugin/public';
|
||||
|
||||
import { useBatchedPublishingSubjects } from '@kbn/presentation-publishing';
|
||||
import { DashboardPanelState } from '../../../../common';
|
||||
import { DashboardGridItem } from './dashboard_grid_item';
|
||||
import { useDashboardGridSettings } from './use_dashboard_grid_settings';
|
||||
import { useDashboardContainer } from '../../embeddable/dashboard_container';
|
||||
import { useDashboardApi } from '../../../dashboard_api/use_dashboard_api';
|
||||
import { getPanelLayoutsAreEqual } from '../../state/diffing/dashboard_diffing_utils';
|
||||
import { DASHBOARD_GRID_HEIGHT, DASHBOARD_MARGIN_SIZE } from '../../../dashboard_constants';
|
||||
|
||||
export const DashboardGrid = ({ viewportWidth }: { viewportWidth: number }) => {
|
||||
const dashboard = useDashboardContainer();
|
||||
const panels = dashboard.select((state) => state.explicitInput.panels);
|
||||
const viewMode = dashboard.select((state) => state.explicitInput.viewMode);
|
||||
const useMargins = dashboard.select((state) => state.explicitInput.useMargins);
|
||||
const expandedPanelId = dashboard.select((state) => state.componentState.expandedPanelId);
|
||||
const focusedPanelId = dashboard.select((state) => state.componentState.focusedPanelId);
|
||||
const animatePanelTransforms = dashboard.select(
|
||||
(state) => state.componentState.animatePanelTransforms
|
||||
);
|
||||
const dashboardApi = useDashboardApi();
|
||||
|
||||
const [animatePanelTransforms, expandedPanelId, focusedPanelId, panels, useMargins, viewMode] =
|
||||
useBatchedPublishingSubjects(
|
||||
dashboardApi.animatePanelTransforms$,
|
||||
dashboardApi.expandedPanelId,
|
||||
dashboardApi.focusedPanelId$,
|
||||
dashboardApi.panels$,
|
||||
dashboardApi.useMargins$,
|
||||
dashboardApi.viewMode
|
||||
);
|
||||
|
||||
/**
|
||||
* Track panel maximized state delayed by one tick and use it to prevent
|
||||
|
@ -96,10 +99,10 @@ export const DashboardGrid = ({ viewportWidth }: { viewportWidth: number }) => {
|
|||
{} as { [key: string]: DashboardPanelState }
|
||||
);
|
||||
if (!getPanelLayoutsAreEqual(panels, updatedPanels)) {
|
||||
dashboard.dispatch.setPanels(updatedPanels);
|
||||
dashboardApi.setPanels(updatedPanels);
|
||||
}
|
||||
},
|
||||
[dashboard, panels, viewMode]
|
||||
[dashboardApi, panels, viewMode]
|
||||
);
|
||||
|
||||
const classes = classNames({
|
||||
|
@ -110,7 +113,7 @@ export const DashboardGrid = ({ viewportWidth }: { viewportWidth: number }) => {
|
|||
'dshLayout-isMaximizedPanel': expandedPanelId !== undefined,
|
||||
});
|
||||
|
||||
const { layouts, breakpoints, columns } = useDashboardGridSettings(panelsInOrder);
|
||||
const { layouts, breakpoints, columns } = useDashboardGridSettings(panelsInOrder, panels);
|
||||
|
||||
// in print mode, dashboard layout is not controlled by React Grid Layout
|
||||
if (viewMode === ViewMode.PRINT) {
|
||||
|
|
|
@ -14,7 +14,8 @@ import { CONTACT_CARD_EMBEDDABLE } from '@kbn/embeddable-plugin/public/lib/test_
|
|||
|
||||
import { buildMockDashboard } from '../../../mocks';
|
||||
import { Item, Props as DashboardGridItemProps } from './dashboard_grid_item';
|
||||
import { DashboardContainerContext } from '../../embeddable/dashboard_container';
|
||||
import { DashboardContext } from '../../../dashboard_api/use_dashboard_api';
|
||||
import { DashboardApi } from '../../../dashboard_api/types';
|
||||
|
||||
jest.mock('@kbn/embeddable-plugin/public', () => {
|
||||
const original = jest.requireActual('@kbn/embeddable-plugin/public');
|
||||
|
@ -44,14 +45,14 @@ const createAndMountDashboardGridItem = (props: DashboardGridItemProps) => {
|
|||
explicitInput: { id: '2' },
|
||||
},
|
||||
};
|
||||
const dashboardContainer = buildMockDashboard({ overrides: { panels } });
|
||||
const dashboardApi = buildMockDashboard({ overrides: { panels } }) as DashboardApi;
|
||||
|
||||
const component = mountWithIntl(
|
||||
<DashboardContainerContext.Provider value={dashboardContainer}>
|
||||
<DashboardContext.Provider value={dashboardApi}>
|
||||
<Item {...props} />
|
||||
</DashboardContainerContext.Provider>
|
||||
</DashboardContext.Provider>
|
||||
);
|
||||
return { dashboardContainer, component };
|
||||
return { dashboardApi, component };
|
||||
};
|
||||
|
||||
test('renders Item', async () => {
|
||||
|
|
|
@ -9,12 +9,13 @@
|
|||
|
||||
import { EuiLoadingChart } from '@elastic/eui';
|
||||
import { css } from '@emotion/react';
|
||||
import { EmbeddablePanel, ReactEmbeddableRenderer, ViewMode } from '@kbn/embeddable-plugin/public';
|
||||
import { EmbeddablePanel, ReactEmbeddableRenderer } from '@kbn/embeddable-plugin/public';
|
||||
import classNames from 'classnames';
|
||||
import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useBatchedPublishingSubjects } from '@kbn/presentation-publishing';
|
||||
import { DashboardPanelState } from '../../../../common';
|
||||
import { pluginServices } from '../../../services/plugin_services';
|
||||
import { useDashboardContainer } from '../../embeddable/dashboard_container';
|
||||
import { useDashboardApi } from '../../../dashboard_api/use_dashboard_api';
|
||||
|
||||
type DivProps = Pick<React.HTMLAttributes<HTMLDivElement>, 'className' | 'style' | 'children'>;
|
||||
|
||||
|
@ -45,10 +46,13 @@ export const Item = React.forwardRef<HTMLDivElement, Props>(
|
|||
},
|
||||
ref
|
||||
) => {
|
||||
const container = useDashboardContainer();
|
||||
const scrollToPanelId = container.select((state) => state.componentState.scrollToPanelId);
|
||||
const highlightPanelId = container.select((state) => state.componentState.highlightPanelId);
|
||||
const useMargins = container.select((state) => state.explicitInput.useMargins);
|
||||
const dashboardApi = useDashboardApi();
|
||||
const [highlightPanelId, scrollToPanelId, useMargins, viewMode] = useBatchedPublishingSubjects(
|
||||
dashboardApi.highlightPanelId$,
|
||||
dashboardApi.scrollToPanelId$,
|
||||
dashboardApi.useMargins$,
|
||||
dashboardApi.viewMode
|
||||
);
|
||||
|
||||
const expandPanel = expandedPanelId !== undefined && expandedPanelId === id;
|
||||
const hidePanel = expandedPanelId !== undefined && expandedPanelId !== id;
|
||||
|
@ -60,17 +64,17 @@ export const Item = React.forwardRef<HTMLDivElement, Props>(
|
|||
'dshDashboardGrid__item--focused': focusPanel,
|
||||
'dshDashboardGrid__item--blurred': blurPanel,
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
printViewport__vis: container.getInput().viewMode === ViewMode.PRINT,
|
||||
printViewport__vis: viewMode === 'print',
|
||||
});
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (typeof ref !== 'function' && ref?.current) {
|
||||
const panelRef = ref.current;
|
||||
if (scrollToPanelId === id) {
|
||||
container.scrollToPanel(panelRef);
|
||||
dashboardApi.scrollToPanel(panelRef);
|
||||
}
|
||||
if (highlightPanelId === id) {
|
||||
container.highlightPanel(panelRef);
|
||||
dashboardApi.highlightPanel(panelRef);
|
||||
}
|
||||
|
||||
panelRef.querySelectorAll('*').forEach((e) => {
|
||||
|
@ -83,7 +87,7 @@ export const Item = React.forwardRef<HTMLDivElement, Props>(
|
|||
}
|
||||
});
|
||||
}
|
||||
}, [id, container, scrollToPanelId, highlightPanelId, ref, blurPanel]);
|
||||
}, [id, dashboardApi, scrollToPanelId, highlightPanelId, ref, blurPanel]);
|
||||
|
||||
const focusStyles = blurPanel
|
||||
? css`
|
||||
|
@ -110,10 +114,10 @@ export const Item = React.forwardRef<HTMLDivElement, Props>(
|
|||
<ReactEmbeddableRenderer
|
||||
type={type}
|
||||
maybeId={id}
|
||||
getParentApi={() => container}
|
||||
getParentApi={() => dashboardApi}
|
||||
key={`${type}_${id}`}
|
||||
panelProps={panelProps}
|
||||
onApiAvailable={(api) => container.registerChildApi(api)}
|
||||
onApiAvailable={(api) => dashboardApi.registerChildApi(api)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -122,11 +126,11 @@ export const Item = React.forwardRef<HTMLDivElement, Props>(
|
|||
<EmbeddablePanel
|
||||
key={type}
|
||||
index={index}
|
||||
embeddable={() => container.untilEmbeddableLoaded(id)}
|
||||
embeddable={() => dashboardApi.untilEmbeddableLoaded(id)}
|
||||
{...panelProps}
|
||||
/>
|
||||
);
|
||||
}, [id, container, type, index, useMargins]);
|
||||
}, [id, dashboardApi, type, index, useMargins]);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
@ -189,14 +193,14 @@ export const DashboardGridItem = React.forwardRef<HTMLDivElement, Props>((props,
|
|||
const {
|
||||
settings: { isProjectEnabledInLabs },
|
||||
} = pluginServices.getServices();
|
||||
const container = useDashboardContainer();
|
||||
const focusedPanelId = container.select((state) => state.componentState.focusedPanelId);
|
||||
const dashboardApi = useDashboardApi();
|
||||
const [focusedPanelId, viewMode] = useBatchedPublishingSubjects(
|
||||
dashboardApi.focusedPanelId$,
|
||||
dashboardApi.viewMode
|
||||
);
|
||||
|
||||
const dashboard = useDashboardContainer();
|
||||
|
||||
const isPrintMode = dashboard.select((state) => state.explicitInput.viewMode) === ViewMode.PRINT;
|
||||
const isEnabled =
|
||||
!isPrintMode &&
|
||||
viewMode !== 'print' &&
|
||||
isProjectEnabledInLabs('labs:dashboard:deferBelowFold') &&
|
||||
(!focusedPanelId || focusedPanelId === props.id);
|
||||
|
||||
|
|
|
@ -12,15 +12,16 @@ import { useMemo } from 'react';
|
|||
import { useEuiTheme } from '@elastic/eui';
|
||||
import { ViewMode } from '@kbn/embeddable-plugin/public';
|
||||
|
||||
import { useStateFromPublishingSubject } from '@kbn/presentation-publishing';
|
||||
import { DashboardPanelMap } from '../../../../common';
|
||||
import { DASHBOARD_GRID_COLUMN_COUNT } from '../../../dashboard_constants';
|
||||
import { useDashboardContainer } from '../../embeddable/dashboard_container';
|
||||
import { useDashboardApi } from '../../../dashboard_api/use_dashboard_api';
|
||||
|
||||
export const useDashboardGridSettings = (panelsInOrder: string[]) => {
|
||||
const dashboard = useDashboardContainer();
|
||||
export const useDashboardGridSettings = (panelsInOrder: string[], panels: DashboardPanelMap) => {
|
||||
const dashboardApi = useDashboardApi();
|
||||
const { euiTheme } = useEuiTheme();
|
||||
|
||||
const panels = dashboard.select((state) => state.explicitInput.panels);
|
||||
const viewMode = dashboard.select((state) => state.explicitInput.viewMode);
|
||||
const viewMode = useStateFromPublishingSubject(dashboardApi.viewMode);
|
||||
|
||||
const layouts = useMemo(() => {
|
||||
return {
|
||||
|
|
|
@ -31,7 +31,7 @@ import {
|
|||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { DashboardContainerInput } from '../../../../common';
|
||||
import { pluginServices } from '../../../services/plugin_services';
|
||||
import { useDashboardContainer } from '../../embeddable/dashboard_container';
|
||||
import { useDashboardApi } from '../../../dashboard_api/use_dashboard_api';
|
||||
|
||||
interface DashboardSettingsProps {
|
||||
onClose: () => void;
|
||||
|
@ -45,19 +45,14 @@ export const DashboardSettings = ({ onClose }: DashboardSettingsProps) => {
|
|||
dashboardContentManagement: { checkForDuplicateDashboardTitle },
|
||||
} = pluginServices.getServices();
|
||||
|
||||
const dashboard = useDashboardContainer();
|
||||
const dashboardApi = useDashboardApi();
|
||||
|
||||
const [dashboardSettingsState, setDashboardSettingsState] = useState({
|
||||
...dashboard.getInput(),
|
||||
});
|
||||
const [localSettings, setLocalSettings] = useState(dashboardApi.getSettings());
|
||||
|
||||
const [isTitleDuplicate, setIsTitleDuplicate] = useState(false);
|
||||
const [isTitleDuplicateConfirmed, setIsTitleDuplicateConfirmed] = useState(false);
|
||||
const [isApplying, setIsApplying] = useState(false);
|
||||
|
||||
const lastSavedId = dashboard.select((state) => state.componentState.lastSavedId);
|
||||
const lastSavedTitle = dashboard.select((state) => state.explicitInput.title);
|
||||
|
||||
const isMounted = useMountedState();
|
||||
|
||||
const onTitleDuplicate = () => {
|
||||
|
@ -69,9 +64,9 @@ export const DashboardSettings = ({ onClose }: DashboardSettingsProps) => {
|
|||
const onApply = async () => {
|
||||
setIsApplying(true);
|
||||
const validTitle = await checkForDuplicateDashboardTitle({
|
||||
title: dashboardSettingsState.title,
|
||||
title: localSettings.title,
|
||||
copyOnSave: false,
|
||||
lastSavedTitle,
|
||||
lastSavedTitle: dashboardApi.panelTitle.value ?? '',
|
||||
onTitleDuplicate,
|
||||
isTitleDuplicateConfirmed,
|
||||
});
|
||||
|
@ -81,15 +76,15 @@ export const DashboardSettings = ({ onClose }: DashboardSettingsProps) => {
|
|||
setIsApplying(false);
|
||||
|
||||
if (validTitle) {
|
||||
dashboard.dispatch.setStateFromSettingsFlyout({ lastSavedId, ...dashboardSettingsState });
|
||||
dashboardApi.setSettings(localSettings);
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
const updateDashboardSetting = useCallback((newSettings: Partial<DashboardContainerInput>) => {
|
||||
setDashboardSettingsState((prevDashboardSettingsState) => {
|
||||
setLocalSettings((prevSettings) => {
|
||||
return {
|
||||
...prevDashboardSettingsState,
|
||||
...prevSettings,
|
||||
...newSettings,
|
||||
};
|
||||
});
|
||||
|
@ -117,7 +112,7 @@ export const DashboardSettings = ({ onClose }: DashboardSettingsProps) => {
|
|||
id="dashboard.embeddableApi.showSettings.flyout.form.duplicateTitleDescription"
|
||||
defaultMessage="Saving ''{title}'' creates a duplicate title."
|
||||
values={{
|
||||
title: dashboardSettingsState.title,
|
||||
title: localSettings.title,
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
|
@ -137,7 +132,7 @@ export const DashboardSettings = ({ onClose }: DashboardSettingsProps) => {
|
|||
}
|
||||
>
|
||||
<components.TagSelector
|
||||
selected={dashboardSettingsState.tags}
|
||||
selected={localSettings.tags}
|
||||
onTagsSelected={(selectedTags) => updateDashboardSetting({ tags: selectedTags })}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
|
@ -173,7 +168,7 @@ export const DashboardSettings = ({ onClose }: DashboardSettingsProps) => {
|
|||
data-test-subj="dashboardTitleInput"
|
||||
name="title"
|
||||
type="text"
|
||||
value={dashboardSettingsState.title}
|
||||
value={localSettings.title}
|
||||
onChange={(event) => {
|
||||
setIsTitleDuplicate(false);
|
||||
setIsTitleDuplicateConfirmed(false);
|
||||
|
@ -201,7 +196,7 @@ export const DashboardSettings = ({ onClose }: DashboardSettingsProps) => {
|
|||
className="dashboardDescriptionInputText"
|
||||
data-test-subj="dashboardDescriptionInput"
|
||||
name="description"
|
||||
value={dashboardSettingsState.description ?? ''}
|
||||
value={localSettings.description ?? ''}
|
||||
onChange={(event) => updateDashboardSetting({ description: event.target.value })}
|
||||
aria-label={i18n.translate(
|
||||
'dashboard.embeddableApi.showSettings.flyout.form.panelDescriptionAriaLabel',
|
||||
|
@ -222,7 +217,7 @@ export const DashboardSettings = ({ onClose }: DashboardSettingsProps) => {
|
|||
>
|
||||
<EuiSwitch
|
||||
data-test-subj="storeTimeWithDashboard"
|
||||
checked={dashboardSettingsState.timeRestore}
|
||||
checked={localSettings.timeRestore}
|
||||
onChange={(event) => updateDashboardSetting({ timeRestore: event.target.checked })}
|
||||
label={
|
||||
<FormattedMessage
|
||||
|
@ -240,7 +235,7 @@ export const DashboardSettings = ({ onClose }: DashboardSettingsProps) => {
|
|||
defaultMessage: 'Use margins between panels',
|
||||
}
|
||||
)}
|
||||
checked={dashboardSettingsState.useMargins}
|
||||
checked={localSettings.useMargins}
|
||||
onChange={(event) => updateDashboardSetting({ useMargins: event.target.checked })}
|
||||
data-test-subj="dashboardMarginsCheckbox"
|
||||
/>
|
||||
|
@ -254,7 +249,7 @@ export const DashboardSettings = ({ onClose }: DashboardSettingsProps) => {
|
|||
defaultMessage: 'Show panel titles',
|
||||
}
|
||||
)}
|
||||
checked={!dashboardSettingsState.hidePanelTitles}
|
||||
checked={!localSettings.hidePanelTitles}
|
||||
onChange={(event) =>
|
||||
updateDashboardSetting({ hidePanelTitles: !event.target.checked })
|
||||
}
|
||||
|
@ -313,7 +308,7 @@ export const DashboardSettings = ({ onClose }: DashboardSettingsProps) => {
|
|||
/>
|
||||
</EuiText>
|
||||
}
|
||||
checked={dashboardSettingsState.syncColors}
|
||||
checked={localSettings.syncColors}
|
||||
onChange={(event) => updateDashboardSetting({ syncColors: event.target.checked })}
|
||||
data-test-subj="dashboardSyncColorsCheckbox"
|
||||
/>
|
||||
|
@ -326,10 +321,10 @@ export const DashboardSettings = ({ onClose }: DashboardSettingsProps) => {
|
|||
defaultMessage: 'Sync cursor across panels',
|
||||
}
|
||||
)}
|
||||
checked={dashboardSettingsState.syncCursor}
|
||||
checked={localSettings.syncCursor}
|
||||
onChange={(event) => {
|
||||
const syncCursor = event.target.checked;
|
||||
if (!syncCursor && dashboardSettingsState.syncTooltips) {
|
||||
if (!syncCursor && localSettings.syncTooltips) {
|
||||
updateDashboardSetting({ syncCursor, syncTooltips: false });
|
||||
} else {
|
||||
updateDashboardSetting({ syncCursor });
|
||||
|
@ -346,8 +341,8 @@ export const DashboardSettings = ({ onClose }: DashboardSettingsProps) => {
|
|||
defaultMessage: 'Sync tooltips across panels',
|
||||
}
|
||||
)}
|
||||
checked={dashboardSettingsState.syncTooltips}
|
||||
disabled={!Boolean(dashboardSettingsState.syncCursor)}
|
||||
checked={localSettings.syncTooltips}
|
||||
disabled={!Boolean(localSettings.syncCursor)}
|
||||
onChange={(event) =>
|
||||
updateDashboardSetting({ syncTooltips: event.target.checked })
|
||||
}
|
||||
|
|
|
@ -22,9 +22,9 @@ import {
|
|||
ControlGroupSerializedState,
|
||||
} from '@kbn/controls-plugin/public';
|
||||
import { CONTROL_GROUP_TYPE } from '@kbn/controls-plugin/common';
|
||||
import { useStateFromPublishingSubject } from '@kbn/presentation-publishing';
|
||||
import { useBatchedPublishingSubjects } from '@kbn/presentation-publishing';
|
||||
import { DashboardGrid } from '../grid';
|
||||
import { useDashboardContainer } from '../../embeddable/dashboard_container';
|
||||
import { useDashboardApi } from '../../../dashboard_api/use_dashboard_api';
|
||||
import { DashboardEmptyScreen } from '../empty_screen/dashboard_empty_screen';
|
||||
|
||||
export const useDebouncedWidthObserver = (skipDebounce = false, wait = 100) => {
|
||||
|
@ -42,17 +42,33 @@ export const useDebouncedWidthObserver = (skipDebounce = false, wait = 100) => {
|
|||
};
|
||||
|
||||
export const DashboardViewportComponent = () => {
|
||||
const dashboard = useDashboardContainer();
|
||||
|
||||
const controlGroupApi = useStateFromPublishingSubject(dashboard.controlGroupApi$);
|
||||
const panelCount = Object.keys(dashboard.select((state) => state.explicitInput.panels)).length;
|
||||
const dashboardApi = useDashboardApi();
|
||||
const [hasControls, setHasControls] = useState(false);
|
||||
const viewMode = dashboard.select((state) => state.explicitInput.viewMode);
|
||||
const dashboardTitle = dashboard.select((state) => state.explicitInput.title);
|
||||
const useMargins = dashboard.select((state) => state.explicitInput.useMargins);
|
||||
const description = dashboard.select((state) => state.explicitInput.description);
|
||||
const focusedPanelId = dashboard.select((state) => state.componentState.focusedPanelId);
|
||||
const expandedPanelId = dashboard.select((state) => state.componentState.expandedPanelId);
|
||||
const [
|
||||
controlGroupApi,
|
||||
dashboardTitle,
|
||||
description,
|
||||
expandedPanelId,
|
||||
focusedPanelId,
|
||||
panels,
|
||||
viewMode,
|
||||
useMargins,
|
||||
uuid,
|
||||
] = useBatchedPublishingSubjects(
|
||||
dashboardApi.controlGroupApi$,
|
||||
dashboardApi.panelTitle,
|
||||
dashboardApi.panelDescription,
|
||||
dashboardApi.expandedPanelId,
|
||||
dashboardApi.focusedPanelId$,
|
||||
dashboardApi.panels$,
|
||||
dashboardApi.viewMode,
|
||||
dashboardApi.useMargins$,
|
||||
dashboardApi.uuid$
|
||||
);
|
||||
|
||||
const panelCount = useMemo(() => {
|
||||
return Object.keys(panels).length;
|
||||
}, [panels]);
|
||||
|
||||
const { ref: resizeRef, width: viewportWidth } = useDebouncedWidthObserver(!!focusedPanelId);
|
||||
|
||||
|
@ -104,19 +120,19 @@ export const DashboardViewportComponent = () => {
|
|||
ControlGroupRuntimeState,
|
||||
ControlGroupApi
|
||||
>
|
||||
key={dashboard.getInput().id}
|
||||
key={uuid}
|
||||
hidePanelChrome={true}
|
||||
panelProps={{ hideLoader: true }}
|
||||
type={CONTROL_GROUP_TYPE}
|
||||
maybeId={'control_group'}
|
||||
getParentApi={() => {
|
||||
return {
|
||||
...dashboard,
|
||||
getSerializedStateForChild: dashboard.getSerializedStateForControlGroup,
|
||||
getRuntimeStateForChild: dashboard.getRuntimeStateForControlGroup,
|
||||
...dashboardApi,
|
||||
getSerializedStateForChild: dashboardApi.getSerializedStateForControlGroup,
|
||||
getRuntimeStateForChild: dashboardApi.getRuntimeStateForControlGroup,
|
||||
};
|
||||
}}
|
||||
onApiAvailable={(api) => dashboard.setControlGroupApi(api)}
|
||||
onApiAvailable={(api) => dashboardApi.setControlGroupApi(api)}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
|
@ -143,11 +159,11 @@ export const DashboardViewportComponent = () => {
|
|||
// because ExitFullScreenButton sets isFullscreenMode to false on unmount while rerendering.
|
||||
// This specifically fixed maximizing/minimizing panels without exiting fullscreen mode.
|
||||
const WithFullScreenButton = ({ children }: { children: JSX.Element }) => {
|
||||
const dashboard = useDashboardContainer();
|
||||
const dashboardApi = useDashboardApi();
|
||||
|
||||
const isFullScreenMode = dashboard.select((state) => state.componentState.fullScreenMode);
|
||||
const isEmbeddedExternally = dashboard.select(
|
||||
(state) => state.componentState.isEmbeddedExternally
|
||||
const [isFullScreenMode, isEmbeddedExternally] = useBatchedPublishingSubjects(
|
||||
dashboardApi.fullScreenMode$,
|
||||
dashboardApi.embeddedExternally$
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -156,7 +172,7 @@ const WithFullScreenButton = ({ children }: { children: JSX.Element }) => {
|
|||
{isFullScreenMode && (
|
||||
<EuiPortal>
|
||||
<ExitFullScreenButton
|
||||
onExit={() => dashboard.dispatch.setFullScreenMode(false)}
|
||||
onExit={() => dashboardApi.setFullScreenMode(false)}
|
||||
toggleChrome={!isEmbeddedExternally}
|
||||
/>
|
||||
</EuiPortal>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
export { showSettings } from './show_settings';
|
||||
export { openSettingsFlyout } from './open_settings_flyout';
|
||||
export { addFromLibrary } from './add_panel_from_library';
|
||||
export { addOrUpdateEmbeddable } from './panel_management';
|
||||
export { runQuickSave, runInteractiveSave } from './run_save_functions';
|
||||
|
|
|
@ -13,37 +13,33 @@ import { toMountPoint } from '@kbn/react-kibana-mount';
|
|||
|
||||
import { pluginServices } from '../../../services/plugin_services';
|
||||
import { DashboardSettings } from '../../component/settings/settings_flyout';
|
||||
import { DashboardContainer, DashboardContainerContext } from '../dashboard_container';
|
||||
import { DashboardContext } from '../../../dashboard_api/use_dashboard_api';
|
||||
import { DashboardApi } from '../../../dashboard_api/types';
|
||||
|
||||
export function showSettings(this: DashboardContainer) {
|
||||
export function openSettingsFlyout(dashboardApi: DashboardApi) {
|
||||
const {
|
||||
analytics,
|
||||
settings: { i18n, theme },
|
||||
overlays,
|
||||
} = pluginServices.getServices();
|
||||
|
||||
// TODO Move this action into DashboardContainer.openOverlay
|
||||
this.dispatch.setHasOverlays(true);
|
||||
|
||||
this.openOverlay(
|
||||
dashboardApi.openOverlay(
|
||||
overlays.openFlyout(
|
||||
toMountPoint(
|
||||
<DashboardContainerContext.Provider value={this}>
|
||||
<DashboardContext.Provider value={dashboardApi}>
|
||||
<DashboardSettings
|
||||
onClose={() => {
|
||||
this.dispatch.setHasOverlays(false);
|
||||
this.clearOverlays();
|
||||
dashboardApi.clearOverlays();
|
||||
}}
|
||||
/>
|
||||
</DashboardContainerContext.Provider>,
|
||||
</DashboardContext.Provider>,
|
||||
{ analytics, i18n, theme }
|
||||
),
|
||||
{
|
||||
size: 's',
|
||||
'data-test-subj': 'dashboardSettingsFlyout',
|
||||
onClose: (flyout) => {
|
||||
this.clearOverlays();
|
||||
this.dispatch.setHasOverlays(false);
|
||||
dashboardApi.clearOverlays();
|
||||
flyout.close();
|
||||
},
|
||||
}
|
|
@ -50,7 +50,7 @@ import { LocatorPublic } from '@kbn/share-plugin/common';
|
|||
import { ExitFullScreenButtonKibanaProvider } from '@kbn/shared-ux-button-exit-full-screen';
|
||||
import deepEqual from 'fast-deep-equal';
|
||||
import { omit } from 'lodash';
|
||||
import React, { createContext, useContext } from 'react';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { batch } from 'react-redux';
|
||||
import { BehaviorSubject, Subject, Subscription, first, skipWhile, switchMap } from 'rxjs';
|
||||
|
@ -59,8 +59,13 @@ import { v4 } from 'uuid';
|
|||
import { PublishesSettings } from '@kbn/presentation-containers/interfaces/publishes_settings';
|
||||
import { apiHasSerializableState } from '@kbn/presentation-containers/interfaces/serialized_state';
|
||||
import { ControlGroupApi, ControlGroupSerializedState } from '@kbn/controls-plugin/public';
|
||||
import { DashboardLocatorParams, DASHBOARD_CONTAINER_TYPE } from '../..';
|
||||
import { DashboardAttributes, DashboardContainerInput, DashboardPanelState } from '../../../common';
|
||||
import { DashboardLocatorParams, DASHBOARD_CONTAINER_TYPE, DashboardApi } from '../..';
|
||||
import {
|
||||
DashboardAttributes,
|
||||
DashboardContainerInput,
|
||||
DashboardPanelMap,
|
||||
DashboardPanelState,
|
||||
} from '../../../common';
|
||||
import {
|
||||
getReferencesForControls,
|
||||
getReferencesForPanelId,
|
||||
|
@ -81,14 +86,13 @@ import { DashboardViewport } from '../component/viewport/dashboard_viewport';
|
|||
import { getDashboardPanelPlacementSetting } from '../panel_placement/panel_placement_registry';
|
||||
import { dashboardContainerReducers } from '../state/dashboard_container_reducers';
|
||||
import { getDiffingMiddleware } from '../state/diffing/dashboard_diffing_integration';
|
||||
import { DashboardPublicState, DashboardReduxState, UnsavedPanelState } from '../types';
|
||||
import {
|
||||
addFromLibrary,
|
||||
addOrUpdateEmbeddable,
|
||||
runQuickSave,
|
||||
runInteractiveSave,
|
||||
showSettings,
|
||||
} from './api';
|
||||
DashboardPublicState,
|
||||
DashboardReduxState,
|
||||
DashboardStateFromSettingsFlyout,
|
||||
UnsavedPanelState,
|
||||
} from '../types';
|
||||
import { addFromLibrary, addOrUpdateEmbeddable, runQuickSave, runInteractiveSave } from './api';
|
||||
import { duplicateDashboardPanel } from './api/duplicate_dashboard_panel';
|
||||
import {
|
||||
combineDashboardFiltersWithControlGroupFilters,
|
||||
|
@ -102,6 +106,7 @@ import {
|
|||
} from './dashboard_container_factory';
|
||||
import { getPanelAddedSuccessString } from '../../dashboard_app/_dashboard_app_strings';
|
||||
import { PANELS_CONTROL_GROUP_KEY } from '../../services/dashboard_backup/dashboard_backup_service';
|
||||
import { DashboardContext } from '../../dashboard_api/use_dashboard_api';
|
||||
|
||||
export interface InheritedChildInput {
|
||||
filters: Filter[];
|
||||
|
@ -124,15 +129,6 @@ type DashboardReduxEmbeddableTools = ReduxEmbeddableTools<
|
|||
typeof dashboardContainerReducers
|
||||
>;
|
||||
|
||||
export const DashboardContainerContext = createContext<DashboardContainer | null>(null);
|
||||
export const useDashboardContainer = (): DashboardContainer => {
|
||||
const dashboard = useContext<DashboardContainer | null>(DashboardContainerContext);
|
||||
if (dashboard == null) {
|
||||
throw new Error('useDashboardContainer must be used inside DashboardContainerContext.');
|
||||
}
|
||||
return dashboard!;
|
||||
};
|
||||
|
||||
export class DashboardContainer
|
||||
extends Container<InheritedChildInput, DashboardContainerInput>
|
||||
implements
|
||||
|
@ -297,6 +293,12 @@ export class DashboardContainer
|
|||
this.dispatch = reduxTools.dispatch;
|
||||
this.select = reduxTools.select;
|
||||
|
||||
this.uuid$ = embeddableInputToSubject<string>(
|
||||
this.publishingSubscription,
|
||||
this,
|
||||
'id'
|
||||
) as BehaviorSubject<string>;
|
||||
|
||||
this.savedObjectId = new BehaviorSubject(this.getDashboardSavedObjectId());
|
||||
this.expandedPanelId = new BehaviorSubject(this.getExpandedPanelId());
|
||||
this.focusedPanelId$ = new BehaviorSubject(this.getState().componentState.focusedPanelId);
|
||||
|
@ -307,6 +309,16 @@ export class DashboardContainer
|
|||
);
|
||||
this.hasUnsavedChanges$ = new BehaviorSubject(this.getState().componentState.hasUnsavedChanges);
|
||||
this.hasOverlays$ = new BehaviorSubject(this.getState().componentState.hasOverlays);
|
||||
this.useMargins$ = new BehaviorSubject(this.getState().explicitInput.useMargins);
|
||||
this.scrollToPanelId$ = new BehaviorSubject(this.getState().componentState.scrollToPanelId);
|
||||
this.highlightPanelId$ = new BehaviorSubject(this.getState().componentState.highlightPanelId);
|
||||
this.animatePanelTransforms$ = new BehaviorSubject(
|
||||
this.getState().componentState.animatePanelTransforms
|
||||
);
|
||||
this.panels$ = new BehaviorSubject(this.getState().explicitInput.panels);
|
||||
this.embeddedExternally$ = new BehaviorSubject(
|
||||
this.getState().componentState.isEmbeddedExternally
|
||||
);
|
||||
this.publishingSubscription.add(
|
||||
this.onStateChange(() => {
|
||||
const state = this.getState();
|
||||
|
@ -334,6 +346,24 @@ export class DashboardContainer
|
|||
if (this.hasOverlays$.value !== state.componentState.hasOverlays) {
|
||||
this.hasOverlays$.next(state.componentState.hasOverlays);
|
||||
}
|
||||
if (this.useMargins$.value !== state.explicitInput.useMargins) {
|
||||
this.useMargins$.next(state.explicitInput.useMargins);
|
||||
}
|
||||
if (this.scrollToPanelId$.value !== state.componentState.scrollToPanelId) {
|
||||
this.scrollToPanelId$.next(state.componentState.scrollToPanelId);
|
||||
}
|
||||
if (this.highlightPanelId$.value !== state.componentState.highlightPanelId) {
|
||||
this.highlightPanelId$.next(state.componentState.highlightPanelId);
|
||||
}
|
||||
if (this.animatePanelTransforms$.value !== state.componentState.animatePanelTransforms) {
|
||||
this.animatePanelTransforms$.next(state.componentState.animatePanelTransforms);
|
||||
}
|
||||
if (this.embeddedExternally$.value !== state.componentState.isEmbeddedExternally) {
|
||||
this.embeddedExternally$.next(state.componentState.isEmbeddedExternally);
|
||||
}
|
||||
if (this.panels$.value !== state.explicitInput.panels) {
|
||||
this.panels$.next(state.explicitInput.panels);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -452,9 +482,9 @@ export class DashboardContainer
|
|||
<ExitFullScreenButtonKibanaProvider
|
||||
coreStart={{ chrome: this.chrome, customBranding: this.customBranding }}
|
||||
>
|
||||
<DashboardContainerContext.Provider value={this}>
|
||||
<DashboardContext.Provider value={this as DashboardApi}>
|
||||
<DashboardViewport />
|
||||
</DashboardContainerContext.Provider>
|
||||
</DashboardContext.Provider>
|
||||
</ExitFullScreenButtonKibanaProvider>
|
||||
</KibanaRenderContextProvider>,
|
||||
dom
|
||||
|
@ -535,7 +565,6 @@ export class DashboardContainer
|
|||
public runInteractiveSave = runInteractiveSave;
|
||||
public runQuickSave = runQuickSave;
|
||||
|
||||
public openSettingsFlyout = showSettings;
|
||||
public addFromLibrary = addFromLibrary;
|
||||
|
||||
public duplicatePanel(id: string) {
|
||||
|
@ -555,6 +584,13 @@ export class DashboardContainer
|
|||
public hasRunMigrations$: BehaviorSubject<boolean | undefined>;
|
||||
public hasUnsavedChanges$: BehaviorSubject<boolean | undefined>;
|
||||
public hasOverlays$: BehaviorSubject<boolean | undefined>;
|
||||
public useMargins$: BehaviorSubject<boolean>;
|
||||
public scrollToPanelId$: BehaviorSubject<string | undefined>;
|
||||
public highlightPanelId$: BehaviorSubject<string | undefined>;
|
||||
public animatePanelTransforms$: BehaviorSubject<boolean | undefined>;
|
||||
public panels$: BehaviorSubject<DashboardPanelMap>;
|
||||
public embeddedExternally$: BehaviorSubject<boolean | undefined>;
|
||||
public uuid$: BehaviorSubject<string>;
|
||||
|
||||
public async replacePanel(idToRemove: string, { panelType, initialState }: PanelPackage) {
|
||||
const newId = await this.replaceEmbeddable(
|
||||
|
@ -812,6 +848,26 @@ export class DashboardContainer
|
|||
return this.getState().explicitInput.panels;
|
||||
};
|
||||
|
||||
public getSettings = (): DashboardStateFromSettingsFlyout => {
|
||||
const state = this.getState();
|
||||
return {
|
||||
description: state.explicitInput.description,
|
||||
hidePanelTitles: state.explicitInput.hidePanelTitles,
|
||||
lastSavedId: state.componentState.lastSavedId,
|
||||
syncColors: state.explicitInput.syncColors,
|
||||
syncCursor: state.explicitInput.syncCursor,
|
||||
syncTooltips: state.explicitInput.syncTooltips,
|
||||
tags: state.explicitInput.tags,
|
||||
timeRestore: state.explicitInput.timeRestore,
|
||||
title: state.explicitInput.title,
|
||||
useMargins: state.explicitInput.useMargins,
|
||||
};
|
||||
};
|
||||
|
||||
public setSettings = (settings: DashboardStateFromSettingsFlyout) => {
|
||||
this.dispatch.setStateFromSettingsFlyout(settings);
|
||||
};
|
||||
|
||||
public setExpandedPanelId = (newId?: string) => {
|
||||
this.dispatch.setExpandedPanelId(newId);
|
||||
};
|
||||
|
@ -925,6 +981,10 @@ export class DashboardContainer
|
|||
this.setScrollToPanelId(id);
|
||||
};
|
||||
|
||||
public setPanels = (panels: DashboardPanelMap) => {
|
||||
this.dispatch.setPanels(panels);
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------
|
||||
// React Embeddable system
|
||||
// ------------------------------------------------------------------------------------------------------
|
||||
|
|
|
@ -48,6 +48,7 @@ import './_dashboard_top_nav.scss';
|
|||
import { DashboardRedirect } from '../dashboard_container/types';
|
||||
import { SaveDashboardReturn } from '../services/dashboard_content_management/types';
|
||||
import { useDashboardApi } from '../dashboard_api/use_dashboard_api';
|
||||
import { openSettingsFlyout } from '../dashboard_container/embeddable/api';
|
||||
|
||||
export interface InternalDashboardTopNavProps {
|
||||
customLeadingBreadCrumbs?: EuiBreadcrumb[];
|
||||
|
@ -188,7 +189,7 @@ export function InternalDashboardTopNav({
|
|||
size="s"
|
||||
type="pencil"
|
||||
className="dshTitleBreadcrumbs__updateIcon"
|
||||
onClick={() => dashboardApi.openSettingsFlyout()}
|
||||
onClick={() => openSettingsFlyout(dashboardApi)}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue