mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-14 08:55:56 +08:00
feat: add functionality to export dashboard as json from listing page
This commit is contained in:
commit
9a1cd65b73
@ -27,6 +27,8 @@ import { AxiosError } from 'axios';
|
||||
import cx from 'classnames';
|
||||
import { ENTITY_VERSION_V4 } from 'constants/app';
|
||||
import ROUTES from 'constants/routes';
|
||||
import { sanitizeDashboardData } from 'container/NewDashboard/DashboardDescription';
|
||||
import { downloadObjectAsJson } from 'container/NewDashboard/DashboardDescription/utils';
|
||||
import { Base64Icons } from 'container/NewDashboard/DashboardSettings/General/utils';
|
||||
import dayjs from 'dayjs';
|
||||
import { useGetAllDashboard } from 'hooks/dashboard/useGetAllDashboard';
|
||||
@ -44,6 +46,7 @@ import {
|
||||
EllipsisVertical,
|
||||
Expand,
|
||||
ExternalLink,
|
||||
FileJson,
|
||||
Github,
|
||||
HdmiPort,
|
||||
LayoutGrid,
|
||||
@ -67,12 +70,18 @@ import {
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { Layout } from 'react-grid-layout';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { generatePath, Link } from 'react-router-dom';
|
||||
import { useCopyToClipboard } from 'react-use';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { Dashboard } from 'types/api/dashboard/getAll';
|
||||
import {
|
||||
Dashboard,
|
||||
IDashboardVariable,
|
||||
WidgetRow,
|
||||
Widgets,
|
||||
} from 'types/api/dashboard/getAll';
|
||||
import AppReducer from 'types/reducer/app';
|
||||
import { isCloudUser } from 'utils/app';
|
||||
|
||||
@ -261,6 +270,11 @@ function DashboardsList(): JSX.Element {
|
||||
isLocked: !!e.isLocked || false,
|
||||
lastUpdatedBy: e.updated_by,
|
||||
image: e.data.image || Base64Icons[0],
|
||||
variables: e.data.variables,
|
||||
widgets: e.data.widgets,
|
||||
layout: e.data.layout,
|
||||
panelMap: e.data.panelMap,
|
||||
version: e.data.version,
|
||||
refetchDashboardList,
|
||||
})) || [];
|
||||
|
||||
@ -413,6 +427,15 @@ function DashboardsList(): JSX.Element {
|
||||
});
|
||||
};
|
||||
|
||||
const handleJsonExport = (event: React.MouseEvent<HTMLElement>): void => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
downloadObjectAsJson(
|
||||
sanitizeDashboardData({ ...dashboard, title: dashboard.name }),
|
||||
dashboard.name,
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="dashboard-list-item" onClick={onClickHandler}>
|
||||
<div className="title-with-action">
|
||||
@ -486,6 +509,14 @@ function DashboardsList(): JSX.Element {
|
||||
>
|
||||
Copy Link
|
||||
</Button>
|
||||
<Button
|
||||
type="text"
|
||||
className="action-btn"
|
||||
icon={<FileJson size={12} />}
|
||||
onClick={handleJsonExport}
|
||||
>
|
||||
Export JSON
|
||||
</Button>
|
||||
</section>
|
||||
<section className="section-2">
|
||||
<DeleteButton
|
||||
@ -504,6 +535,7 @@ function DashboardsList(): JSX.Element {
|
||||
<EllipsisVertical
|
||||
className="dashboard-action-icon"
|
||||
size={14}
|
||||
data-testid="dashboard-action-icon"
|
||||
onClick={(e): void => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
@ -1068,6 +1100,11 @@ export interface Data {
|
||||
isLocked: boolean;
|
||||
id: string;
|
||||
image?: string;
|
||||
widgets?: Array<WidgetRow | Widgets>;
|
||||
layout?: Layout[];
|
||||
panelMap?: Record<string, { widgets: Layout[]; collapsed: boolean }>;
|
||||
variables: Record<string, IDashboardVariable>;
|
||||
version?: string;
|
||||
}
|
||||
|
||||
export default DashboardsList;
|
||||
|
@ -65,7 +65,7 @@ interface DashboardDescriptionProps {
|
||||
handle: FullScreenHandle;
|
||||
}
|
||||
|
||||
function sanitizeDashboardData(
|
||||
export function sanitizeDashboardData(
|
||||
selectedData: DashboardData,
|
||||
): Omit<DashboardData, 'uuid'> {
|
||||
if (!selectedData?.variables) {
|
||||
|
@ -1,13 +1,21 @@
|
||||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
import ROUTES from 'constants/routes';
|
||||
import DashboardsList from 'container/ListOfDashboard';
|
||||
import { dashboardEmptyState } from 'mocks-server/__mockdata__/dashboards';
|
||||
import * as dashboardUtils from 'container/NewDashboard/DashboardDescription';
|
||||
import {
|
||||
dashboardEmptyState,
|
||||
dashboardSuccessResponse,
|
||||
} from 'mocks-server/__mockdata__/dashboards';
|
||||
import { server } from 'mocks-server/server';
|
||||
import { rest } from 'msw';
|
||||
import { DashboardProvider } from 'providers/Dashboard/Dashboard';
|
||||
import { MemoryRouter, useLocation } from 'react-router-dom';
|
||||
import { fireEvent, render, waitFor } from 'tests/test-utils';
|
||||
|
||||
jest.mock('container/NewDashboard/DashboardDescription', () => ({
|
||||
sanitizeDashboardData: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
...jest.requireActual('react-router-dom'),
|
||||
useLocation: jest.fn(),
|
||||
@ -204,4 +212,26 @@ describe('dashboard list page', () => {
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it('ensure that the export JSON popover action works correctly', async () => {
|
||||
const { getByText, getAllByTestId } = render(<DashboardsList />);
|
||||
|
||||
await waitFor(() => {
|
||||
const popovers = getAllByTestId('dashboard-action-icon');
|
||||
expect(popovers).toHaveLength(dashboardSuccessResponse.data.length);
|
||||
fireEvent.click([...popovers[0].children][0]);
|
||||
});
|
||||
|
||||
const exportJsonBtn = getByText('Export JSON');
|
||||
expect(exportJsonBtn).toBeInTheDocument();
|
||||
fireEvent.click(exportJsonBtn);
|
||||
const firstDashboardData = dashboardSuccessResponse.data[0];
|
||||
expect(dashboardUtils.sanitizeDashboardData).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
id: firstDashboardData.uuid,
|
||||
title: firstDashboardData.data.title,
|
||||
createdAt: firstDashboardData.created_at,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user