Skip to content

Commit

Permalink
Add Export flyout to dashboards
Browse files Browse the repository at this point in the history
  • Loading branch information
nickpeihl committed Dec 12, 2024
1 parent ba945c9 commit cca4f65
Show file tree
Hide file tree
Showing 10 changed files with 368 additions and 150 deletions.
1 change: 1 addition & 0 deletions src/plugins/dashboard/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export { prefixReferencesFromPanel } from './dashboard_container/persistable_sta
export {
convertPanelsArrayToPanelMap,
convertPanelMapToPanelsArray,
generateNewPanelIds,
} from './lib/dashboard_panel_converters';

export const UI_SETTINGS = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,14 @@ export function getDashboardApi({
unifiedSearchManager.internalApi.controlGroupReload$,
unifiedSearchManager.internalApi.panelsReload$
).pipe(debounceTime(0)),
getDashboardState: async () => {
const { controlGroupReferences, dashboardState, panelReferences } = await getState();
return getDashboardContentManagementService().getDashboardState({
controlGroupReferences,
currentState: dashboardState,
panelReferences,
});
},
runInteractiveSave: async () => {
trackOverlayApi.clearOverlays();
const saveResult = await openSaveModal({
Expand Down
7 changes: 6 additions & 1 deletion src/plugins/dashboard/public/dashboard_api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,9 @@ import { IKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public';
import { PublishesReload } from '@kbn/presentation-publishing/interfaces/fetch/publishes_reload';
import { PublishesSearchSession } from '@kbn/presentation-publishing/interfaces/fetch/publishes_search_session';
import { LocatorPublic } from '@kbn/share-plugin/common';
import type { SavedObjectReference } from '@kbn/core-saved-objects-api-server';
import { DashboardPanelMap, DashboardPanelState } from '../../common';
import type { DashboardOptions } from '../../server/content_management';
import type { DashboardAttributes, DashboardOptions } from '../../server/content_management';
import {
LoadDashboardReturn,
SaveDashboardReturn,
Expand Down Expand Up @@ -153,6 +154,10 @@ export type DashboardApi = CanExpandPanels &
focusedPanelId$: PublishingSubject<string | undefined>;
forceRefresh: () => void;
getSettings: () => DashboardStateFromSettingsFlyout;
getDashboardState: () => Promise<{
attributes: DashboardAttributes;
references: SavedObjectReference[];
}>;
getDashboardPanelFromId: (id: string) => Promise<DashboardPanelState>;
hasOverlays$: PublishingSubject<boolean>;
hasUnsavedChanges$: PublishingSubject<boolean>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,14 @@ export const topNavStrings = {
defaultMessage: 'Switch to edit mode',
}),
},
export: {
label: i18n.translate('dashboard.topNav.exportButtonAriaLabel', {
defaultMessage: 'export',
}),
description: i18n.translate('dashboard.topNav.exportConfigDescription', {
defaultMessage: 'Export Dashboard',
}),
},
quickSave: {
label: i18n.translate('dashboard.topNave.saveButtonAriaLabel', {
defaultMessage: 'save',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import React, { useEffect } from 'react';
import { css } from '@emotion/react';
import { FormattedMessage } from '@kbn/i18n-react';

import {
EuiCallOut,
EuiCodeBlock,
EuiEmptyPrompt,
EuiFlyoutBody,
EuiFlyoutHeader,
EuiLoadingChart,
EuiTitle,
} from '@elastic/eui';
import { DashboardApi } from '../../..';

const flyoutBodyCss = css`
height: 100%;
.euiFlyoutBody__overflowContent {
height: 100%;
padding: 0;
}
`;

export interface DashboardExportFlyoutProps {
close: () => void;
getDashboardState: DashboardApi['getDashboardState'];
}

export const DashboardExportFlyout: React.FC<DashboardExportFlyoutProps> = ({
close,
getDashboardState,
}) => {
const [{ data: dashboard, loading, error }, setDashboardState] = React.useState<{
loading: boolean;
data: Awaited<ReturnType<DashboardApi['getDashboardState']>> | null;
error: unknown | null;
}>({ loading: true, data: null, error: null });

useEffect(() => {
const loadDashboardState = () => {
getDashboardState()
.then((_dashboard) =>
setDashboardState((prevState) => ({
...prevState,
loading: false,
data: _dashboard,
}))
)
.catch((err) =>
setDashboardState((prevState) => ({
...prevState,
loading: false,
error: err,
}))
);
};

loadDashboardState();
}, [getDashboardState]);

return (
<>
<EuiFlyoutHeader hasBorder>
<EuiTitle size="s">
<h1 id="exportDashboardFlyout">
<FormattedMessage
id="dashboard.topNav.exportFlyoutTitle"
defaultMessage="Export dashboard"
/>
</h1>
</EuiTitle>
</EuiFlyoutHeader>
<EuiFlyoutBody css={flyoutBodyCss}>
{error ? (
<EuiCallOut
title={
<FormattedMessage
id="dashboard.topNav.exportFlyoutError"
defaultMessage="An error occurred"
/>
}
color="danger"
iconType="alert"
/>
) : (
<>
{loading ? (
<EuiEmptyPrompt
data-test-subj="dashboardExportLoadingIndicator"
icon={<EuiLoadingChart size="l" mono />}
/>
) : (
<EuiCodeBlock language="json" isCopyable overflowHeight="100%" isVirtualized>
{JSON.stringify(dashboard, null, 2)}
</EuiCodeBlock>
)}
</>
)}
</EuiFlyoutBody>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import { Dispatch, SetStateAction, useCallback, useMemo, useState } from 'react';
import React, { Dispatch, SetStateAction, useCallback, useMemo, useState } from 'react';

import { ViewMode } from '@kbn/embeddable-plugin/public';
import type { TopNavMenuData } from '@kbn/navigation-plugin/public';
import useMountedState from 'react-use/lib/useMountedState';

import { useBatchedPublishingSubjects } from '@kbn/presentation-publishing';
import { toMountPoint } from '@kbn/react-kibana-mount';
import { UI_SETTINGS } from '../../../common';
import { useDashboardApi } from '../../dashboard_api/use_dashboard_api';
import { CHANGE_CHECK_DEBOUNCE } from '../../dashboard_constants';
Expand All @@ -25,6 +26,7 @@ import { coreServices, shareService } from '../../services/kibana_services';
import { getDashboardCapabilities } from '../../utils/get_dashboard_capabilities';
import { topNavStrings } from '../_dashboard_app_strings';
import { ShowShareModal } from './share/show_share_modal';
import { DashboardExportFlyout } from './export/show_export_flyout';

export const useDashboardMenuItems = ({
isLabsShown,
Expand Down Expand Up @@ -69,6 +71,34 @@ export const useDashboardMenuItems = ({
[dashboardTitle, hasUnsavedChanges, lastSavedId, dashboardApi]
);

/**
* Show Export flyout
*/
const showExport = useCallback(() => {
dashboardApi.openOverlay(
coreServices.overlays.openFlyout(
toMountPoint(
<DashboardExportFlyout
getDashboardState={dashboardApi.getDashboardState}
close={() => dashboardApi.clearOverlays()}
/>,
{
i18n: coreServices.i18n,
theme: coreServices.theme,
}
),
{
size: 'm',
'data-test-subj': 'dashboardExportFlyout',
onClose: (flyout) => {
dashboardApi.clearOverlays();
flyout.close();
},
}
)
);
}, [dashboardApi]);

/**
* Save the dashboard without any UI or popups.
*/
Expand Down Expand Up @@ -199,6 +229,14 @@ export const useDashboardMenuItems = ({
run: showShare,
} as TopNavMenuData,

export: {
...topNavStrings.export,
id: 'export',
testId: 'dashboardExportMenuItem',
disableButton: disableTopNav,
run: showExport,
},

settings: {
...topNavStrings.settings,
id: 'settings',
Expand All @@ -215,6 +253,7 @@ export const useDashboardMenuItems = ({
dashboardInteractiveSave,
viewMode,
showShare,
showExport,
dashboardApi,
setIsLabsShown,
isLabsShown,
Expand Down Expand Up @@ -264,6 +303,7 @@ export const useDashboardMenuItems = ({
...labsMenuItem,
menuItems.fullScreen,
...shareMenuItem,
menuItems.export,
...duplicateMenuItem,
...mayberesetChangesMenuItem,
...editMenuItem,
Expand All @@ -286,7 +326,13 @@ export const useDashboardMenuItems = ({
} else {
editModeItems.push(menuItems.switchToViewMode, menuItems.interactiveSave);
}
return [...labsMenuItem, menuItems.settings, ...shareMenuItem, ...editModeItems];
return [
...labsMenuItem,
menuItems.settings,
...shareMenuItem,
menuItems.export,
...editModeItems,
];
}, [isLabsEnabled, menuItems, lastSavedId, showResetChange, resetChangesMenuItem]);

return { viewModeTopNavConfig, editModeTopNavConfig };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
findDashboardsByIds,
searchDashboards,
} from './lib/find_dashboards';
import { getDashboardState } from './lib/get_dashboard_state';
import { loadDashboardState } from './lib/load_dashboard_state';
import { saveDashboardState } from './lib/save_dashboard_state';
import { updateDashboardMeta } from './lib/update_dashboard_meta';
Expand All @@ -32,6 +33,7 @@ export const getDashboardContentManagementService = () => {
return {
loadDashboardState,
saveDashboardState,
getDashboardState,
findDashboards: {
search: searchDashboards,
findById: findDashboardById,
Expand Down
Loading

0 comments on commit cca4f65

Please sign in to comment.