Commit d2155823 by Hugo Häggmark Committed by GitHub

TextPanel: Adds proper editor for markdown and html (#25618)

parent 9a8289b6
...@@ -74,9 +74,24 @@ const expectDrawerTabsAndContent = () => { ...@@ -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'; ...@@ -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> { ...@@ -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
......
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>> = ({ ...@@ -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 (
......
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'; ...@@ -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) ...@@ -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);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment