mirror of
https://github.com/grafana/grafana.git
synced 2025-02-10 23:55:47 -06:00
Correlations: add tracking for add, update, delete, and details expanded (#58239)
* Correlations: add tracking for add, update, delete, and details expanded * add tests * change delete event * rename handlers
This commit is contained in:
parent
96cdf77995
commit
e4c394dfcd
@ -8,7 +8,14 @@ import { MockDataSourceApi } from 'test/mocks/datasource_srv';
|
||||
import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock';
|
||||
|
||||
import { DataSourcePluginMeta } from '@grafana/data';
|
||||
import { BackendSrv, FetchError, FetchResponse, setDataSourceSrv, BackendSrvRequest } from '@grafana/runtime';
|
||||
import {
|
||||
BackendSrv,
|
||||
FetchError,
|
||||
FetchResponse,
|
||||
setDataSourceSrv,
|
||||
BackendSrvRequest,
|
||||
reportInteraction,
|
||||
} from '@grafana/runtime';
|
||||
import { GrafanaContext } from 'app/core/context/GrafanaContext';
|
||||
import { contextSrv } from 'app/core/services/context_srv';
|
||||
import { configureStore } from 'app/store/configureStore';
|
||||
@ -47,12 +54,6 @@ function createFetchError(overrides?: DeepPartial<FetchError>): FetchError {
|
||||
);
|
||||
}
|
||||
|
||||
jest.mock('app/core/services/context_srv');
|
||||
|
||||
const mocks = {
|
||||
contextSrv: jest.mocked(contextSrv),
|
||||
};
|
||||
|
||||
const renderWithContext = async (
|
||||
datasources: ConstructorParameters<typeof MockDataSourceSrv>[0] = {},
|
||||
correlations: Correlation[] = []
|
||||
@ -217,6 +218,20 @@ const renderWithContext = async (
|
||||
return renderResult;
|
||||
};
|
||||
|
||||
jest.mock('app/core/services/context_srv');
|
||||
|
||||
const mocks = {
|
||||
contextSrv: jest.mocked(contextSrv),
|
||||
reportInteraction: jest.fn(),
|
||||
};
|
||||
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
reportInteraction: (...args: Parameters<typeof reportInteraction>) => {
|
||||
mocks.reportInteraction(...args);
|
||||
},
|
||||
}));
|
||||
|
||||
beforeAll(() => {
|
||||
mocks.contextSrv.hasPermission.mockImplementation(() => true);
|
||||
});
|
||||
@ -254,6 +269,10 @@ describe('CorrelationsPage', () => {
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mocks.reportInteraction.mockClear();
|
||||
});
|
||||
|
||||
it('shows CTA', async () => {
|
||||
// insert form should not be present
|
||||
expect(screen.queryByRole('button', { name: /add$/i })).not.toBeInTheDocument();
|
||||
@ -305,12 +324,18 @@ describe('CorrelationsPage', () => {
|
||||
// Waits for the form to be removed, meaning the correlation got successfully saved
|
||||
await waitForElementToBeRemoved(() => screen.queryByRole('button', { name: /add$/i }));
|
||||
|
||||
expect(mocks.reportInteraction).toHaveBeenLastCalledWith('grafana_correlations_added');
|
||||
|
||||
// the table showing correlations should have appeared
|
||||
expect(screen.getByRole('table')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('With correlations', () => {
|
||||
afterEach(() => {
|
||||
mocks.reportInteraction.mockClear();
|
||||
});
|
||||
|
||||
let queryRowsByCellValue: (columnName: Matcher, textValue: Matcher) => HTMLTableRowElement[];
|
||||
let getHeaderByName: (columnName: Matcher) => HTMLTableCellElement;
|
||||
let queryCellsByColumnName: (columnName: Matcher) => HTMLTableCellElement[];
|
||||
@ -430,6 +455,8 @@ describe('CorrelationsPage', () => {
|
||||
|
||||
// the form should get removed after successful submissions
|
||||
await waitForElementToBeRemoved(() => screen.queryByRole('button', { name: /add$/i }));
|
||||
|
||||
expect(mocks.reportInteraction).toHaveBeenLastCalledWith('grafana_correlations_added');
|
||||
});
|
||||
|
||||
it('correctly closes the form when clicking on the close icon', async () => {
|
||||
@ -460,6 +487,8 @@ describe('CorrelationsPage', () => {
|
||||
fireEvent.click(confirmButton);
|
||||
|
||||
await waitForElementToBeRemoved(() => screen.queryByRole('cell', { name: /some label$/i }));
|
||||
|
||||
expect(mocks.reportInteraction).toHaveBeenLastCalledWith('grafana_correlations_deleted');
|
||||
});
|
||||
|
||||
it('correctly edits correlations', async () => {
|
||||
@ -468,6 +497,8 @@ describe('CorrelationsPage', () => {
|
||||
const rowExpanderButton = within(tableRows[0]).getByRole('button', { name: /toggle row expanded/i });
|
||||
fireEvent.click(rowExpanderButton);
|
||||
|
||||
expect(mocks.reportInteraction).toHaveBeenLastCalledWith('grafana_correlations_details_expanded');
|
||||
|
||||
await waitForElementToBeRemoved(() => screen.queryByText(/loading query editor/i));
|
||||
|
||||
fireEvent.change(screen.getByRole('textbox', { name: /label/i }), { target: { value: 'edited label' } });
|
||||
@ -482,6 +513,8 @@ describe('CorrelationsPage', () => {
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole('cell', { name: /edited label$/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(mocks.reportInteraction).toHaveBeenLastCalledWith('grafana_correlations_edited');
|
||||
});
|
||||
});
|
||||
|
||||
@ -526,6 +559,8 @@ describe('CorrelationsPage', () => {
|
||||
|
||||
fireEvent.click(rowExpanderButton);
|
||||
|
||||
expect(mocks.reportInteraction).toHaveBeenLastCalledWith('grafana_correlations_details_expanded');
|
||||
|
||||
// wait for the form to be rendered and query editor to be mounted
|
||||
await waitForElementToBeRemoved(() => screen.queryByText(/loading query editor/i));
|
||||
|
||||
|
@ -4,7 +4,7 @@ import React, { memo, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { CellProps, SortByFn } from 'react-table';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { isFetchError } from '@grafana/runtime';
|
||||
import { isFetchError, reportInteraction } from '@grafana/runtime';
|
||||
import { Badge, Button, DeleteButton, HorizontalGroup, LoadingPlaceholder, useStyles2, Alert } from '@grafana/ui';
|
||||
import { Page } from 'app/core/components/Page/Page';
|
||||
import { contextSrv } from 'app/core/core';
|
||||
@ -15,6 +15,7 @@ import { AddCorrelationForm } from './Forms/AddCorrelationForm';
|
||||
import { EditCorrelationForm } from './Forms/EditCorrelationForm';
|
||||
import { EmptyCorrelationsCTA } from './components/EmptyCorrelationsCTA';
|
||||
import { Column, Table } from './components/Table';
|
||||
import type { RemoveCorrelationParams } from './types';
|
||||
import { CorrelationData, useCorrelations } from './useCorrelations';
|
||||
|
||||
const sortDatasource: SortByFn<CorrelationData> = (a, b, column) =>
|
||||
@ -43,11 +44,31 @@ export default function CorrelationsPage() {
|
||||
|
||||
const canWriteCorrelations = contextSrv.hasPermission(AccessControlAction.DataSourcesWrite);
|
||||
|
||||
const handleAdd = useCallback(() => {
|
||||
const handleAdded = useCallback(() => {
|
||||
reportInteraction('grafana_correlations_added');
|
||||
fetchCorrelations();
|
||||
setIsAdding(false);
|
||||
}, [fetchCorrelations]);
|
||||
|
||||
const handleUpdated = useCallback(() => {
|
||||
reportInteraction('grafana_correlations_edited');
|
||||
fetchCorrelations();
|
||||
}, [fetchCorrelations]);
|
||||
|
||||
const handleDelete = useCallback(
|
||||
(params: RemoveCorrelationParams) => {
|
||||
remove.execute(params);
|
||||
},
|
||||
[remove]
|
||||
);
|
||||
|
||||
// onDelete - triggers when deleting a correlation
|
||||
useEffect(() => {
|
||||
if (remove.value) {
|
||||
reportInteraction('grafana_correlations_deleted');
|
||||
}
|
||||
}, [remove.value]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!remove.error && !remove.loading && remove.value) {
|
||||
fetchCorrelations();
|
||||
@ -66,11 +87,11 @@ export default function CorrelationsPage() {
|
||||
!readOnly && (
|
||||
<DeleteButton
|
||||
aria-label="delete correlation"
|
||||
onConfirm={() => remove.execute({ sourceUID, uid })}
|
||||
onConfirm={() => handleDelete({ sourceUID, uid })}
|
||||
closeOnConfirm
|
||||
/>
|
||||
),
|
||||
[remove]
|
||||
[handleDelete]
|
||||
);
|
||||
|
||||
const columns = useMemo<Array<Column<CorrelationData>>>(
|
||||
@ -142,15 +163,15 @@ export default function CorrelationsPage() {
|
||||
)
|
||||
}
|
||||
|
||||
{isAdding && <AddCorrelationForm onClose={() => setIsAdding(false)} onCreated={handleAdd} />}
|
||||
{isAdding && <AddCorrelationForm onClose={() => setIsAdding(false)} onCreated={handleAdded} />}
|
||||
|
||||
{data && data.length >= 1 && (
|
||||
<Table
|
||||
renderExpandedRow={({ target, source, ...correlation }) => (
|
||||
<EditCorrelationForm
|
||||
correlation={{ ...correlation, sourceUID: source.uid, targetUID: target.uid }}
|
||||
onUpdated={fetchCorrelations}
|
||||
readOnly={isSourceReadOnly({ source }) || !canWriteCorrelations}
|
||||
renderExpandedRow={(correlation) => (
|
||||
<ExpendedRow
|
||||
correlation={correlation}
|
||||
onUpdated={handleUpdated}
|
||||
readOnly={isSourceReadOnly({ source: correlation.source }) || !canWriteCorrelations}
|
||||
/>
|
||||
)}
|
||||
columns={columns}
|
||||
@ -164,6 +185,28 @@ export default function CorrelationsPage() {
|
||||
);
|
||||
}
|
||||
|
||||
interface ExpandedRowProps {
|
||||
correlation: CorrelationData;
|
||||
readOnly: boolean;
|
||||
onUpdated: () => void;
|
||||
}
|
||||
function ExpendedRow({ correlation: { source, target, ...correlation }, readOnly, onUpdated }: ExpandedRowProps) {
|
||||
useEffect(
|
||||
() => reportInteraction('grafana_correlations_details_expanded'),
|
||||
// we only want to fire this on first render
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<EditCorrelationForm
|
||||
correlation={{ ...correlation, sourceUID: source.uid, targetUID: target.uid }}
|
||||
onUpdated={onUpdated}
|
||||
readOnly={readOnly}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const getDatasourceCellStyles = (theme: GrafanaTheme2) => ({
|
||||
root: css`
|
||||
display: flex;
|
||||
|
@ -48,7 +48,7 @@ export const useCorrelations = () => {
|
||||
[backend]
|
||||
);
|
||||
|
||||
const [removeInfo, remove] = useAsyncFn<(params: RemoveCorrelationParams) => Promise<void>>(
|
||||
const [removeInfo, remove] = useAsyncFn<(params: RemoveCorrelationParams) => Promise<{ message: string }>>(
|
||||
({ sourceUID, uid }) => backend.delete(`/api/datasources/uid/${sourceUID}/correlations/${uid}`),
|
||||
[backend]
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user