123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- import React, { FC, useEffect, useState } from 'react';
- import { selectors } from '@grafana/e2e-selectors';
- import { DataSourcePicker } from '@grafana/runtime';
- import { ExpressionDatasourceRef } from '@grafana/runtime/src/utils/DataSourceWithBackend';
- import {
- Button,
- Field,
- FormAPI,
- FormFieldErrors,
- FormsOnSubmit,
- HorizontalGroup,
- Input,
- InputControl,
- Legend,
- } from '@grafana/ui';
- import { FolderPicker } from 'app/core/components/Select/FolderPicker';
- import {
- DashboardInput,
- DashboardInputs,
- DataSourceInput,
- ImportDashboardDTO,
- LibraryPanelInputState,
- } from '../state/reducers';
- import { validateTitle, validateUid } from '../utils/validation';
- import { ImportDashboardLibraryPanelsList } from './ImportDashboardLibraryPanelsList';
- interface Props extends Pick<FormAPI<ImportDashboardDTO>, 'register' | 'errors' | 'control' | 'getValues' | 'watch'> {
- uidReset: boolean;
- inputs: DashboardInputs;
- initialFolderId: number;
- onCancel: () => void;
- onUidReset: () => void;
- onSubmit: FormsOnSubmit<ImportDashboardDTO>;
- }
- export const ImportDashboardForm: FC<Props> = ({
- register,
- errors,
- control,
- getValues,
- uidReset,
- inputs,
- initialFolderId,
- onUidReset,
- onCancel,
- onSubmit,
- watch,
- }) => {
- const [isSubmitted, setSubmitted] = useState(false);
- const watchDataSources = watch('dataSources');
- const watchFolder = watch('folder');
- /*
- This useEffect is needed for overwriting a dashboard. It
- submits the form even if there's validation errors on title or uid.
- */
- useEffect(() => {
- if (isSubmitted && (errors.title || errors.uid)) {
- onSubmit(getValues(), {} as any);
- }
- }, [errors, getValues, isSubmitted, onSubmit]);
- const newLibraryPanels = inputs?.libraryPanels?.filter((i) => i.state === LibraryPanelInputState.New) ?? [];
- const existingLibraryPanels = inputs?.libraryPanels?.filter((i) => i.state === LibraryPanelInputState.Exists) ?? [];
- return (
- <>
- <Legend>Options</Legend>
- <Field label="Name" invalid={!!errors.title} error={errors.title && errors.title.message}>
- <Input
- {...register('title', {
- required: 'Name is required',
- validate: async (v: string) => await validateTitle(v, getValues().folder.id),
- })}
- type="text"
- data-testid={selectors.components.ImportDashboardForm.name}
- />
- </Field>
- <Field label="Folder">
- <InputControl
- render={({ field: { ref, ...field } }) => (
- <FolderPicker {...field} enableCreateNew initialFolderId={initialFolderId} />
- )}
- name="folder"
- control={control}
- />
- </Field>
- <Field
- label="Unique identifier (UID)"
- description="The unique identifier (UID) of a dashboard can be used for uniquely identify a dashboard between multiple Grafana installs.
- The UID allows having consistent URLs for accessing dashboards so changing the title of a dashboard will not break any
- bookmarked links to that dashboard."
- invalid={!!errors.uid}
- error={errors.uid && errors.uid.message}
- >
- <>
- {!uidReset ? (
- <Input
- disabled
- {...register('uid', { validate: async (v: string) => await validateUid(v) })}
- addonAfter={!uidReset && <Button onClick={onUidReset}>Change uid</Button>}
- />
- ) : (
- <Input {...register('uid', { required: true, validate: async (v: string) => await validateUid(v) })} />
- )}
- </>
- </Field>
- {inputs.dataSources &&
- inputs.dataSources.map((input: DataSourceInput, index: number) => {
- if (input.pluginId === ExpressionDatasourceRef.type) {
- return null;
- }
- const dataSourceOption = `dataSources[${index}]`;
- const current = watchDataSources ?? [];
- return (
- <Field
- label={input.label}
- key={dataSourceOption}
- invalid={errors.dataSources && !!errors.dataSources[index]}
- error={errors.dataSources && errors.dataSources[index] && 'A data source is required'}
- >
- <InputControl
- name={dataSourceOption as any}
- render={({ field: { ref, ...field } }) => (
- <DataSourcePicker
- {...field}
- noDefault={true}
- placeholder={input.info}
- pluginId={input.pluginId}
- current={current[index]?.uid}
- />
- )}
- control={control}
- rules={{ required: true }}
- />
- </Field>
- );
- })}
- {inputs.constants &&
- inputs.constants.map((input: DashboardInput, index) => {
- const constantIndex = `constants[${index}]`;
- return (
- <Field
- label={input.label}
- error={errors.constants && errors.constants[index] && `${input.label} needs a value`}
- invalid={errors.constants && !!errors.constants[index]}
- key={constantIndex}
- >
- <Input {...register(constantIndex as any, { required: true })} defaultValue={input.value} />
- </Field>
- );
- })}
- <ImportDashboardLibraryPanelsList
- inputs={newLibraryPanels}
- label="New library panels"
- description="List of new library panels that will get imported."
- folderName={watchFolder.title}
- />
- <ImportDashboardLibraryPanelsList
- inputs={existingLibraryPanels}
- label="Existing library panels"
- description="List of existing library panels. These panels are not affected by the import."
- folderName={watchFolder.title}
- />
- <HorizontalGroup>
- <Button
- type="submit"
- data-testid={selectors.components.ImportDashboardForm.submit}
- variant={getButtonVariant(errors)}
- onClick={() => {
- setSubmitted(true);
- }}
- >
- {getButtonText(errors)}
- </Button>
- <Button type="reset" variant="secondary" onClick={onCancel}>
- Cancel
- </Button>
- </HorizontalGroup>
- </>
- );
- };
- function getButtonVariant(errors: FormFieldErrors<ImportDashboardDTO>) {
- return errors && (errors.title || errors.uid) ? 'destructive' : 'primary';
- }
- function getButtonText(errors: FormFieldErrors<ImportDashboardDTO>) {
- return errors && (errors.title || errors.uid) ? 'Import (Overwrite)' : 'Import';
- }
|