mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
TextPanel: Adds proper editor for markdown and html (#25618)
This commit is contained in:
parent
9a8289b6d9
commit
d21558231f
@ -74,9 +74,24 @@ const expectDrawerTabsAndContent = () => {
|
|||||||
e2e.components.PanelInspector.Stats.content().should('not.be.visible');
|
e2e.components.PanelInspector.Stats.content().should('not.be.visible');
|
||||||
e2e.components.PanelInspector.Query.content().should('not.be.visible');
|
e2e.components.PanelInspector.Query.content().should('not.be.visible');
|
||||||
|
|
||||||
|
e2e().wait(250);
|
||||||
|
/* Monaco Editor specific wait that fixes error below https://github.com/microsoft/monaco-editor/issues/354
|
||||||
|
TypeError: Cannot read property 'getText' of null
|
||||||
|
at Object.getFoldingRanges (http://localhost:3001/public/build/json.worker.js:18829:102)
|
||||||
|
at JSONWorker.getFoldingRanges (http://localhost:3001/public/build/json.worker.js:23818:40)
|
||||||
|
at EditorSimpleWorker.fmr (http://localhost:3001/public/build/json.worker.js:10407:58)
|
||||||
|
at SimpleWorkerServer._handleMessage (http://localhost:3001/public/build/json.worker.js:6939:59)
|
||||||
|
at Object.handleMessage (http://localhost:3001/public/build/json.worker.js:6920:22)
|
||||||
|
at SimpleWorkerProtocol._handleMessage (http://localhost:3001/public/build/json.worker.js:6757:32)
|
||||||
|
at SimpleWorkerProtocol.handleMessage (http://localhost:3001/public/build/json.worker.js:6719:10)
|
||||||
|
at SimpleWorkerServer.onmessage (http://localhost:3001/public/build/json.worker.js:6926:20)
|
||||||
|
at self.onmessage (http://localhost:3001/public/build/json.worker.js:12050:18)
|
||||||
|
*/
|
||||||
|
|
||||||
e2e.components.Tab.title('Query')
|
e2e.components.Tab.title('Query')
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
e2e.components.PanelInspector.Query.content().should('be.visible');
|
e2e.components.PanelInspector.Query.content().should('be.visible');
|
||||||
e2e.components.PanelInspector.Data.content().should('not.be.visible');
|
e2e.components.PanelInspector.Data.content().should('not.be.visible');
|
||||||
e2e.components.PanelInspector.Stats.content().should('not.be.visible');
|
e2e.components.PanelInspector.Stats.content().should('not.be.visible');
|
||||||
|
@ -3,17 +3,18 @@ import { ComponentType } from 'react';
|
|||||||
import { FieldConfigOptionsRegistry } from './FieldConfigOptionsRegistry';
|
import { FieldConfigOptionsRegistry } from './FieldConfigOptionsRegistry';
|
||||||
import { DataFrame, InterpolateFunction, VariableSuggestionsScope, VariableSuggestion } from '../types';
|
import { DataFrame, InterpolateFunction, VariableSuggestionsScope, VariableSuggestion } from '../types';
|
||||||
|
|
||||||
export interface StandardEditorContext {
|
export interface StandardEditorContext<TOptions> {
|
||||||
data?: DataFrame[]; // All results
|
data?: DataFrame[]; // All results
|
||||||
replaceVariables?: InterpolateFunction;
|
replaceVariables?: InterpolateFunction;
|
||||||
getSuggestions?: (scope?: VariableSuggestionsScope) => VariableSuggestion[];
|
getSuggestions?: (scope?: VariableSuggestionsScope) => VariableSuggestion[];
|
||||||
|
options?: TOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StandardEditorProps<TValue = any, TSettings = any> {
|
export interface StandardEditorProps<TValue = any, TSettings = any, TOptions = any> {
|
||||||
value: TValue;
|
value: TValue;
|
||||||
onChange: (value?: TValue) => void;
|
onChange: (value?: TValue) => void;
|
||||||
item: StandardEditorsRegistryItem<TValue, TSettings>;
|
item: StandardEditorsRegistryItem<TValue, TSettings>;
|
||||||
context: StandardEditorContext;
|
context: StandardEditorContext<TOptions>;
|
||||||
}
|
}
|
||||||
export interface StandardEditorsRegistryItem<TValue = any, TSettings = any> extends RegistryItem {
|
export interface StandardEditorsRegistryItem<TValue = any, TSettings = any> extends RegistryItem {
|
||||||
editor: ComponentType<StandardEditorProps<TValue, TSettings>>;
|
editor: ComponentType<StandardEditorProps<TValue, TSettings>>;
|
||||||
|
@ -22,7 +22,7 @@ export interface FieldConfigSource<TOptions extends object = any> {
|
|||||||
overrides: ConfigOverrideRule[];
|
overrides: ConfigOverrideRule[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FieldOverrideContext extends StandardEditorContext {
|
export interface FieldOverrideContext extends StandardEditorContext<any> {
|
||||||
field?: Field;
|
field?: Field;
|
||||||
dataFrameIndex?: number; // The index for the selected field frame
|
dataFrameIndex?: number; // The index for the selected field frame
|
||||||
data: DataFrame[]; // All results
|
data: DataFrame[]; // All results
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import {
|
import {
|
||||||
|
DataFrame,
|
||||||
|
InterpolateFunction,
|
||||||
PanelOptionsEditorItem,
|
PanelOptionsEditorItem,
|
||||||
PanelPlugin,
|
PanelPlugin,
|
||||||
DataFrame,
|
|
||||||
StandardEditorContext,
|
StandardEditorContext,
|
||||||
InterpolateFunction,
|
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { get as lodashGet, set as lodashSet } from 'lodash';
|
import { get as lodashGet, set as lodashSet } from 'lodash';
|
||||||
import { Field, Label } from '@grafana/ui';
|
import { Field, Label } from '@grafana/ui';
|
||||||
@ -37,9 +37,10 @@ export const PanelOptionsEditor: React.FC<PanelOptionsEditorProps<any>> = ({
|
|||||||
onChange(newOptions);
|
onChange(newOptions);
|
||||||
};
|
};
|
||||||
|
|
||||||
const context: StandardEditorContext = {
|
const context: StandardEditorContext<any> = {
|
||||||
data: data ?? [],
|
data: data ?? [],
|
||||||
replaceVariables,
|
replaceVariables,
|
||||||
|
options,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
46
public/app/plugins/panel/text/TextPanelEditor.tsx
Normal file
46
public/app/plugins/panel/text/TextPanelEditor.tsx
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import React, { FC, useMemo } from 'react';
|
||||||
|
import { css, cx } from 'emotion';
|
||||||
|
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||||
|
import { CodeEditor, stylesFactory, useTheme } from '@grafana/ui';
|
||||||
|
import { GrafanaTheme, StandardEditorProps } from '@grafana/data';
|
||||||
|
|
||||||
|
import { TextOptions } from './types';
|
||||||
|
|
||||||
|
export const TextPanelEditor: FC<StandardEditorProps<string, any, TextOptions>> = ({ value, onChange, context }) => {
|
||||||
|
const language = useMemo(() => context.options?.mode ?? 'markdown', [context]);
|
||||||
|
const theme = useTheme();
|
||||||
|
const styles = getStyles(theme);
|
||||||
|
return (
|
||||||
|
<div className={cx(styles.editorBox)}>
|
||||||
|
<AutoSizer disableHeight>
|
||||||
|
{({ width }) => {
|
||||||
|
if (width === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CodeEditor
|
||||||
|
value={value}
|
||||||
|
onBlur={onChange}
|
||||||
|
onSave={onChange}
|
||||||
|
language={language}
|
||||||
|
width={width}
|
||||||
|
showMiniMap={false}
|
||||||
|
height="200px"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</AutoSizer>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStyles = stylesFactory((theme: GrafanaTheme) => ({
|
||||||
|
editorBox: css`
|
||||||
|
label: editorBox;
|
||||||
|
border: ${theme.border.width.sm} solid ${theme.colors.border2};
|
||||||
|
border-radius: ${theme.border.radius.sm};
|
||||||
|
margin: ${theme.spacing.xs} 0;
|
||||||
|
width: 100%;
|
||||||
|
`,
|
||||||
|
}));
|
@ -3,6 +3,7 @@ import { PanelPlugin } from '@grafana/data';
|
|||||||
import { TextPanel } from './TextPanel';
|
import { TextPanel } from './TextPanel';
|
||||||
import { TextOptions } from './types';
|
import { TextOptions } from './types';
|
||||||
import { textPanelMigrationHandler } from './textPanelMigrationHandler';
|
import { textPanelMigrationHandler } from './textPanelMigrationHandler';
|
||||||
|
import { TextPanelEditor } from './TextPanelEditor';
|
||||||
|
|
||||||
export const plugin = new PanelPlugin<TextOptions>(TextPanel)
|
export const plugin = new PanelPlugin<TextOptions>(TextPanel)
|
||||||
.setPanelOptions(builder => {
|
.setPanelOptions(builder => {
|
||||||
@ -19,18 +20,16 @@ export const plugin = new PanelPlugin<TextOptions>(TextPanel)
|
|||||||
},
|
},
|
||||||
defaultValue: 'markdown',
|
defaultValue: 'markdown',
|
||||||
})
|
})
|
||||||
.addTextInput({
|
.addCustomEditor({
|
||||||
|
id: 'content',
|
||||||
path: 'content',
|
path: 'content',
|
||||||
name: 'Content',
|
name: 'Content',
|
||||||
description: 'Content of the panel',
|
description: 'Content of the panel',
|
||||||
settings: {
|
|
||||||
useTextarea: true,
|
|
||||||
rows: 5,
|
|
||||||
},
|
|
||||||
defaultValue: `# Title
|
defaultValue: `# Title
|
||||||
|
|
||||||
For markdown syntax help: [commonmark.org/help](https://commonmark.org/help/)
|
For markdown syntax help: [commonmark.org/help](https://commonmark.org/help/)
|
||||||
`,
|
`,
|
||||||
|
editor: TextPanelEditor,
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.setMigrationHandler(textPanelMigrationHandler);
|
.setMigrationHandler(textPanelMigrationHandler);
|
||||||
|
Loading…
Reference in New Issue
Block a user