Panel Inspect: use monaco for json display (#25251)

This commit is contained in:
Ryan McKinley
2020-06-29 10:58:47 -07:00
committed by GitHub
parent dcd5752086
commit 1a711e7df0
16 changed files with 341 additions and 56 deletions
@@ -180,6 +180,8 @@ const getBaseWebpackConfig: WebpackConfigurationGetter = async options => {
'@grafana/ui',
'@grafana/runtime',
'@grafana/data',
'monaco-editor',
'react-monaco-editor',
// @ts-ignore
(context, request, callback) => {
const prefix = 'grafana/';
@@ -78,12 +78,36 @@ module.exports = ({ config, mode }) => {
config.optimization = {
nodeEnv: 'production',
moduleIds: 'hashed',
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
minChunks: 1,
cacheGroups: {
monaco: {
test: /[\\/]node_modules[\\/](monaco-editor)[\\/].*[jt]sx?$/,
chunks: 'initial',
priority: 20,
enforce: true,
},
vendors: {
test: /[\\/]node_modules[\\/].*[jt]sx?$/,
chunks: 'initial',
priority: -10,
reuseExistingChunk: true,
enforce: true,
},
default: {
priority: -20,
chunks: 'all',
test: /.*[jt]sx?$/,
reuseExistingChunk: true,
},
},
},
minimize: true,
minimizer: [
new TerserPlugin({
cache: false,
parallel: false,
sourceMap: false,
}),
new TerserPlugin({ cache: false, parallel: false, sourceMap: false, exclude: /monaco/ }),
new OptimizeCSSAssetsPlugin({}),
],
};
+2
View File
@@ -47,6 +47,8 @@
"immutable": "3.8.2",
"jquery": "3.5.1",
"lodash": "4.17.15",
"monaco-editor": "0.20.0",
"react-monaco-editor": "0.36.0",
"moment": "2.24.0",
"papaparse": "4.6.3",
"rc-cascader": "1.0.1",
+10 -1
View File
@@ -25,7 +25,16 @@ const buildCjsPackage = ({ env }) => {
},
},
],
external: ['react', 'react-dom', '@grafana/data', 'moment', '@grafana/e2e-selectors'],
external: [
'react',
'react-dom',
'@grafana/data',
'@grafana/e2e-selectors',
'moment',
'monaco-editor', // Monaco should not be used directly
'monaco-editor/esm/vs/editor/editor.api', // Monaco should not be used directly
'react-monaco-editor',
],
plugins: [
commonjs({
include: /node_modules/,
@@ -0,0 +1,8 @@
import { Meta, Story, Preview, Props } from '@storybook/addon-docs/blocks';
import { CodeEditor } from './CodeEditor';
<Meta title="MDX|CodeEditor" component={CodeEditor} />
# CodeEditor
Monaco Code editor
@@ -0,0 +1,42 @@
import React from 'react';
import { text } from '@storybook/addon-knobs';
import { action } from '@storybook/addon-actions';
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
import mdx from './CodeEditor.mdx';
import CodeEditor from './CodeEditor';
const getKnobs = () => {
return {
text: text('Body', 'SELECT * FROM table LIMIT 10'),
language: text('Language', 'sql'),
};
};
export default {
title: 'CodeEditor',
component: CodeEditor,
decorators: [withCenteredStory],
parameters: {
docs: {
page: mdx,
},
},
};
export const basic = () => {
const { text, language } = getKnobs();
return (
<CodeEditor
value={text}
language={language}
onBlur={(text: string) => {
console.log('Blur: ', text);
action('code blur')(text);
}}
onSave={(text: string) => {
console.log('Save: ', text);
action('code saved')(text);
}}
/>
);
};
@@ -0,0 +1,92 @@
import React from 'react';
import { withTheme } from '../../themes';
import { Themeable } from '../../types';
import { KeyCode, editor, KeyMod } from 'monaco-editor/esm/vs/editor/editor.api';
import ReactMonaco from 'react-monaco-editor';
export interface CodeEditorProps {
value: string;
language: string;
width?: number | string;
height?: number | string;
readOnly?: boolean;
showMiniMap?: boolean;
/**
* Callback after the editor has mounted that gives you raw access to monaco
*
* @experimental
*/
onEditorDidMount?: (editor: editor.IStandaloneCodeEditor) => void;
/** Handler to be performed when editor is blurred */
onBlur?: CodeEditorChangeHandler;
/** Handler to be performed when Cmd/Ctrl+S is pressed */
onSave?: CodeEditorChangeHandler;
}
type Props = CodeEditorProps & Themeable;
class UnthemedCodeEditor extends React.PureComponent<Props> {
getEditorValue = () => '';
onBlur = () => {
const { onBlur } = this.props;
if (onBlur) {
onBlur(this.getEditorValue());
}
};
editorDidMount = (editor: editor.IStandaloneCodeEditor) => {
const { onSave, onEditorDidMount } = this.props;
this.getEditorValue = () => editor.getValue();
if (onSave) {
editor.addCommand(KeyMod.CtrlCmd | KeyCode.KEY_S, () => {
onSave(this.getEditorValue());
});
}
if (onEditorDidMount) {
onEditorDidMount(editor);
}
};
render() {
const { theme, language, width, height, showMiniMap, readOnly } = this.props;
const value = this.props.value ?? '';
const longText = value.length > 100;
return (
<div onBlur={this.onBlur}>
<ReactMonaco
width={width}
height={height}
language={language}
theme={theme.isDark ? 'vs-dark' : 'vs-light'}
value={value}
options={{
wordWrap: 'off',
codeLens: false, // not included in the bundle
minimap: {
enabled: longText && showMiniMap,
renderCharacters: false,
},
readOnly,
lineNumbersMinChars: 4,
lineDecorationsWidth: 0,
overviewRulerBorder: false,
automaticLayout: true,
}}
editorDidMount={this.editorDidMount}
/>
</div>
);
}
}
export type CodeEditorChangeHandler = (value: string) => void;
export default withTheme(UnthemedCodeEditor);
@@ -0,0 +1,29 @@
import React from 'react';
import { useAsyncDependency } from '../../utils/useAsyncDependency';
import { ErrorWithStack, LoadingPlaceholder } from '..';
import { CodeEditorProps } from './CodeEditor';
export type CodeEditorChangeHandler = (value: string) => void;
export const CodeEditor: React.FC<CodeEditorProps> = props => {
const { loading, error, dependency } = useAsyncDependency(
import(/* webpackChunkName: "code-editor" */ './CodeEditor')
);
if (loading) {
return <LoadingPlaceholder text={'Loading...'} />;
}
if (error) {
return (
<ErrorWithStack
title="Code editor failed to load"
error={error}
errorInfo={{ componentStack: error?.stack || '' }}
/>
);
}
const CodeEditor = dependency.default;
return <CodeEditor {...props} />;
};
@@ -34,6 +34,7 @@ export { FilterPill } from './FilterPill/FilterPill';
export { ConfirmModal } from './ConfirmModal/ConfirmModal';
export { QueryField } from './QueryField/QueryField';
export { CodeEditor } from './Monaco/CodeEditorLazy';
// TODO: namespace
export { Modal } from './Modal/Modal';
@@ -0,0 +1,13 @@
import { useAsync } from 'react-use';
// Allows simple dynamic imports in the components
export const useAsyncDependency = (importStatement: Promise<any>) => {
const state = useAsync(async () => {
return await importStatement;
});
return {
...state,
dependency: state.value,
};
};