mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
* update eslint, tsconfig + esbuild to handle new jsx transform * remove thing that breaks the new jsx transform * remove react imports * adjust grafana-icons build * is this the correct syntax? * try this * well this was much easier than expected... * change grafana-plugin-configs webpack config * fixes * fix lockfile * fix 2 more violations * use path.resolve instead of require.resolve * remove react import * fix react imports * more fixes * remove React import * remove import React from docs * remove another react import
271 lines
8.8 KiB
TypeScript
271 lines
8.8 KiB
TypeScript
import { css } from '@emotion/css';
|
|
import { PureComponent } from 'react';
|
|
import { connect, ConnectedProps } from 'react-redux';
|
|
|
|
import { AppEvents, GrafanaTheme2, LoadingState, NavModelItem } from '@grafana/data';
|
|
import { selectors } from '@grafana/e2e-selectors';
|
|
import { config, reportInteraction } from '@grafana/runtime';
|
|
import {
|
|
Button,
|
|
Field,
|
|
Input,
|
|
Spinner,
|
|
stylesFactory,
|
|
TextArea,
|
|
Themeable2,
|
|
FileDropzone,
|
|
withTheme2,
|
|
DropzoneFile,
|
|
FileDropzoneDefaultChildren,
|
|
LinkButton,
|
|
TextLink,
|
|
Label,
|
|
Stack,
|
|
} from '@grafana/ui';
|
|
import appEvents from 'app/core/app_events';
|
|
import { Form } from 'app/core/components/Form/Form';
|
|
import { Page } from 'app/core/components/Page/Page';
|
|
import { t, Trans } from 'app/core/internationalization';
|
|
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
|
|
import { StoreState } from 'app/types';
|
|
|
|
import { cleanUpAction } from '../../core/actions/cleanUp';
|
|
|
|
import { ImportDashboardOverview } from './components/ImportDashboardOverview';
|
|
import { fetchGcomDashboard, importDashboardJson } from './state/actions';
|
|
import { initialImportDashboardState } from './state/reducers';
|
|
import { validateDashboardJson, validateGcomDashboard } from './utils/validation';
|
|
|
|
type DashboardImportPageRouteSearchParams = {
|
|
gcomDashboardId?: string;
|
|
};
|
|
|
|
type OwnProps = Themeable2 & GrafanaRouteComponentProps<{}, DashboardImportPageRouteSearchParams>;
|
|
|
|
const IMPORT_STARTED_EVENT_NAME = 'dashboard_import_loaded';
|
|
const JSON_PLACEHOLDER = `{
|
|
"title": "Example - Repeating Dictionary variables",
|
|
"uid": "_0HnEoN4z",
|
|
"panels": [...]
|
|
...
|
|
}
|
|
`;
|
|
|
|
const mapStateToProps = (state: StoreState) => ({
|
|
loadingState: state.importDashboard.state,
|
|
});
|
|
|
|
const mapDispatchToProps = {
|
|
fetchGcomDashboard,
|
|
importDashboardJson,
|
|
cleanUpAction,
|
|
};
|
|
|
|
const connector = connect(mapStateToProps, mapDispatchToProps);
|
|
|
|
type Props = OwnProps & ConnectedProps<typeof connector>;
|
|
|
|
class UnthemedDashboardImport extends PureComponent<Props> {
|
|
constructor(props: Props) {
|
|
super(props);
|
|
const { gcomDashboardId } = this.props.queryParams;
|
|
if (gcomDashboardId) {
|
|
this.getGcomDashboard({ gcomDashboard: gcomDashboardId });
|
|
return;
|
|
}
|
|
}
|
|
|
|
componentWillUnmount() {
|
|
this.props.cleanUpAction({ cleanupAction: (state) => (state.importDashboard = initialImportDashboardState) });
|
|
}
|
|
|
|
// Do not display upload file list
|
|
fileListRenderer = (file: DropzoneFile, removeFile: (file: DropzoneFile) => void) => null;
|
|
|
|
onFileUpload = (result: string | ArrayBuffer | null) => {
|
|
reportInteraction(IMPORT_STARTED_EVENT_NAME, {
|
|
import_source: 'json_uploaded',
|
|
});
|
|
|
|
try {
|
|
this.props.importDashboardJson(JSON.parse(String(result)));
|
|
} catch (error) {
|
|
if (error instanceof Error) {
|
|
appEvents.emit(AppEvents.alertError, ['Import failed', 'JSON -> JS Serialization failed: ' + error.message]);
|
|
}
|
|
return;
|
|
}
|
|
};
|
|
|
|
getDashboardFromJson = (formData: { dashboardJson: string }) => {
|
|
reportInteraction(IMPORT_STARTED_EVENT_NAME, {
|
|
import_source: 'json_pasted',
|
|
});
|
|
|
|
this.props.importDashboardJson(JSON.parse(formData.dashboardJson));
|
|
};
|
|
|
|
getGcomDashboard = (formData: { gcomDashboard: string }) => {
|
|
reportInteraction(IMPORT_STARTED_EVENT_NAME, {
|
|
import_source: 'gcom',
|
|
});
|
|
|
|
let dashboardId;
|
|
const match = /(^\d+$)|dashboards\/(\d+)/.exec(formData.gcomDashboard);
|
|
if (match && match[1]) {
|
|
dashboardId = match[1];
|
|
} else if (match && match[2]) {
|
|
dashboardId = match[2];
|
|
}
|
|
|
|
if (dashboardId) {
|
|
this.props.fetchGcomDashboard(dashboardId);
|
|
}
|
|
};
|
|
|
|
renderImportForm() {
|
|
const styles = importStyles(this.props.theme);
|
|
|
|
const GcomDashboardsLink = () => (
|
|
<TextLink variant="bodySmall" href="https://grafana.com/grafana/dashboards/" external>
|
|
grafana.com/dashboards
|
|
</TextLink>
|
|
);
|
|
|
|
return (
|
|
<>
|
|
<div className={styles.option}>
|
|
<FileDropzone
|
|
options={{ multiple: false, accept: ['.json', '.txt'] }}
|
|
readAs="readAsText"
|
|
fileListRenderer={this.fileListRenderer}
|
|
onLoad={this.onFileUpload}
|
|
>
|
|
<FileDropzoneDefaultChildren
|
|
primaryText={t('dashboard-import.file-dropzone.primary-text', 'Upload dashboard JSON file')}
|
|
secondaryText={t(
|
|
'dashboard-import.file-dropzone.secondary-text',
|
|
'Drag and drop here or click to browse'
|
|
)}
|
|
/>
|
|
</FileDropzone>
|
|
</div>
|
|
<div className={styles.option}>
|
|
<Form onSubmit={this.getGcomDashboard} defaultValues={{ gcomDashboard: '' }}>
|
|
{({ register, errors }) => (
|
|
<Field
|
|
label={
|
|
<Label className={styles.labelWithLink} htmlFor="url-input">
|
|
<span>
|
|
<Trans i18nKey="dashboard-import.gcom-field.label">
|
|
Find and import dashboards for common applications at <GcomDashboardsLink />
|
|
</Trans>
|
|
</span>
|
|
</Label>
|
|
}
|
|
invalid={!!errors.gcomDashboard}
|
|
error={errors.gcomDashboard && errors.gcomDashboard.message}
|
|
>
|
|
<Input
|
|
id="url-input"
|
|
placeholder={t('dashboard-import.gcom-field.placeholder', 'Grafana.com dashboard URL or ID')}
|
|
type="text"
|
|
{...register('gcomDashboard', {
|
|
required: t(
|
|
'dashboard-import.gcom-field.validation-required',
|
|
'A Grafana dashboard URL or ID is required'
|
|
),
|
|
validate: validateGcomDashboard,
|
|
})}
|
|
addonAfter={
|
|
<Button type="submit">
|
|
<Trans i18nKey="dashboard-import.gcom-field.load-button">Load</Trans>
|
|
</Button>
|
|
}
|
|
/>
|
|
</Field>
|
|
)}
|
|
</Form>
|
|
</div>
|
|
<div className={styles.option}>
|
|
<Form onSubmit={this.getDashboardFromJson} defaultValues={{ dashboardJson: '' }}>
|
|
{({ register, errors }) => (
|
|
<>
|
|
<Field
|
|
label={t('dashboard-import.json-field.label', 'Import via dashboard JSON model')}
|
|
invalid={!!errors.dashboardJson}
|
|
error={errors.dashboardJson && errors.dashboardJson.message}
|
|
>
|
|
<TextArea
|
|
{...register('dashboardJson', {
|
|
required: t('dashboard-import.json-field.validation-required', 'Need a dashboard JSON model'),
|
|
validate: validateDashboardJson,
|
|
})}
|
|
data-testid={selectors.components.DashboardImportPage.textarea}
|
|
id="dashboard-json-textarea"
|
|
rows={10}
|
|
placeholder={JSON_PLACEHOLDER}
|
|
/>
|
|
</Field>
|
|
<Stack>
|
|
<Button type="submit" data-testid={selectors.components.DashboardImportPage.submit}>
|
|
<Trans i18nKey="dashboard-import.form-actions.load">Load</Trans>
|
|
</Button>
|
|
<LinkButton variant="secondary" href={`${config.appSubUrl}/dashboards`}>
|
|
<Trans i18nKey="dashboard-import.form-actions.cancel">Cancel</Trans>
|
|
</LinkButton>
|
|
</Stack>
|
|
</>
|
|
)}
|
|
</Form>
|
|
</div>
|
|
</>
|
|
);
|
|
}
|
|
|
|
pageNav: NavModelItem = {
|
|
text: 'Import dashboard',
|
|
subTitle: 'Import dashboard from file or Grafana.com',
|
|
};
|
|
|
|
render() {
|
|
const { loadingState } = this.props;
|
|
|
|
return (
|
|
<Page navId="dashboards/browse" pageNav={this.pageNav}>
|
|
<Page.Contents>
|
|
{loadingState === LoadingState.Loading && (
|
|
<Stack direction={'column'} justifyContent="center">
|
|
<Stack justifyContent="center">
|
|
<Spinner size="xxl" />
|
|
</Stack>
|
|
</Stack>
|
|
)}
|
|
{[LoadingState.Error, LoadingState.NotStarted].includes(loadingState) && this.renderImportForm()}
|
|
{loadingState === LoadingState.Done && <ImportDashboardOverview />}
|
|
</Page.Contents>
|
|
</Page>
|
|
);
|
|
}
|
|
}
|
|
|
|
const DashboardImportUnConnected = withTheme2(UnthemedDashboardImport);
|
|
const DashboardImport = connector(DashboardImportUnConnected);
|
|
DashboardImport.displayName = 'DashboardImport';
|
|
export default DashboardImport;
|
|
|
|
const importStyles = stylesFactory((theme: GrafanaTheme2) => {
|
|
return {
|
|
option: css`
|
|
margin-bottom: ${theme.spacing(4)};
|
|
max-width: 600px;
|
|
`,
|
|
labelWithLink: css`
|
|
max-width: 100%;
|
|
`,
|
|
linkWithinLabel: css`
|
|
font-size: inherit;
|
|
`,
|
|
};
|
|
});
|