diff --git a/packages/grafana-e2e-selectors/src/selectors/components.ts b/packages/grafana-e2e-selectors/src/selectors/components.ts index 766c4dbf36d..24e61fd2841 100644 --- a/packages/grafana-e2e-selectors/src/selectors/components.ts +++ b/packages/grafana-e2e-selectors/src/selectors/components.ts @@ -1288,6 +1288,9 @@ export const versionedComponents = { submit: { [MIN_GRAFANA_VERSION]: 'data-testid-import-dashboard-submit', }, + floatGridItemsWarning: { + [MIN_GRAFANA_VERSION]: 'data-testid-import-dashboard-float-grid-items-warning', + }, }, PanelAlertTabContent: { content: { diff --git a/public/app/features/manage-dashboards/import/components/ImportDashboardFormV2.test.tsx b/public/app/features/manage-dashboards/import/components/ImportDashboardFormV2.test.tsx new file mode 100644 index 00000000000..c6ac4ce9d96 --- /dev/null +++ b/public/app/features/manage-dashboards/import/components/ImportDashboardFormV2.test.tsx @@ -0,0 +1,113 @@ +import { render, screen } from '@testing-library/react'; + +import { selectors } from '@grafana/e2e-selectors'; +import { defaultSpec, Spec as DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2'; +import { Form } from 'app/core/components/Form/Form'; + +import { DashboardInputs, InputType, ImportFormDataV2 } from '../../types'; + +import { ImportDashboardFormV2 } from './ImportDashboardFormV2'; + +const mockInputs: DashboardInputs = { + dataSources: [ + { + name: 'Prometheus', + label: 'Prometheus', + pluginId: 'prometheus', + type: InputType.DataSource, + description: 'Select a Prometheus data source', + info: 'Select prometheus', + value: '', + }, + { + name: 'Loki', + label: 'Loki', + pluginId: 'loki', + type: InputType.DataSource, + description: 'Select a Loki data source', + info: 'Select loki', + value: '', + }, + ], + constants: [], + libraryPanels: [], +}; + +jest.mock('../utils/validation', () => ({ + validateTitle: jest.fn().mockResolvedValue(true), +})); + +jest.mock('app/core/components/Select/FolderPicker', () => ({ + FolderPicker: ({ value, onChange }: { value: string; onChange: (val: string, title: string) => void }) => ( + onChange(e.target.value, 'Test Folder')} /> + ), +})); + +jest.mock('app/features/datasources/components/picker/DataSourcePicker', () => ({ + DataSourcePicker: ({ + onChange, + pluginId, + current, + }: { + onChange: (ds: { uid: string; type: string; name: string }) => void; + pluginId: string; + current?: { uid: string; type: string }; + }) => ( + + onChange({ + uid: e.target.value, + type: pluginId, + name: `${pluginId} instance`, + }) + } + /> + ), +})); + +describe('ImportDashboardFormV2', () => { + const mockOnCancel = jest.fn(); + const mockOnSubmit = jest.fn(); + + function renderForm(hasFloatGridItems = false, inputs: DashboardInputs = mockInputs) { + const defaultDashboard: DashboardV2Spec = defaultSpec(); + return render( + + onSubmit={mockOnSubmit} + defaultValues={{ dashboard: defaultDashboard, folderUid: 'test-folder' }} + > + {({ register, errors, control, watch, getValues }) => ( + + )} + + ); + } + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders float grid items warning when hasFloatGridItems is true', () => { + renderForm(true); + expect(screen.queryByTestId(selectors.components.ImportDashboardForm.floatGridItemsWarning)).toBeInTheDocument(); + }); + + it('does not render float grid items warning when hasFloatGridItems is false', () => { + renderForm(false); + expect( + screen.queryByTestId(selectors.components.ImportDashboardForm.floatGridItemsWarning) + ).not.toBeInTheDocument(); + }); +}); diff --git a/public/app/features/manage-dashboards/import/components/ImportDashboardFormV2.tsx b/public/app/features/manage-dashboards/import/components/ImportDashboardFormV2.tsx index 3d5899dda98..ac604a49f1d 100644 --- a/public/app/features/manage-dashboards/import/components/ImportDashboardFormV2.tsx +++ b/public/app/features/manage-dashboards/import/components/ImportDashboardFormV2.tsx @@ -4,7 +4,7 @@ import { Controller, FieldErrors, FieldPath, UseFormReturn } from 'react-hook-fo import { selectors } from '@grafana/e2e-selectors'; import { Trans, t } from '@grafana/i18n'; import { ExpressionDatasourceRef } from '@grafana/runtime/internal'; -import { Button, Field, FormFieldErrors, FormsOnSubmit, Stack, Input } from '@grafana/ui'; +import { Button, Field, FormFieldErrors, FormsOnSubmit, Stack, Input, Alert } from '@grafana/ui'; import { FolderPicker } from 'app/core/components/Select/FolderPicker'; import { DataSourcePicker } from 'app/features/datasources/components/picker/DataSourcePicker'; @@ -16,9 +16,19 @@ interface Props extends Pick, 'register' | 'cont errors: FieldErrors; onCancel: () => void; onSubmit: FormsOnSubmit; + hasFloatGridItems: boolean; } -export const ImportDashboardFormV2 = ({ register, errors, control, inputs, getValues, onCancel, onSubmit }: Props) => { +export const ImportDashboardFormV2 = ({ + register, + errors, + control, + inputs, + getValues, + onCancel, + onSubmit, + hasFloatGridItems, +}: Props) => { const [isSubmitted, setSubmitted] = useState(false); const [selectedDataSources, setSelectedDataSources] = useState>({}); @@ -119,6 +129,19 @@ export const ImportDashboardFormV2 = ({ register, errors, control, inputs, getVa ); })} + {hasFloatGridItems && ( + + + The dashboard contains grid items with floating positions. This is not supported by Grafana and the numbers + will be truncated to integers. + + + )} +