/usr/share/grafana/public/app/plugins/panel/text
import { css, cx } from '@emotion/css'; import DangerouslySetHtmlContent from 'dangerously-set-html-content'; import { useState } from 'react'; import { useDebounce } from 'react-use'; import { GrafanaTheme2, PanelProps, renderTextPanelMarkdown, textUtil, InterpolateFunction } from '@grafana/data'; import { CodeEditor, ScrollContainer, useStyles2 } from '@grafana/ui'; import config from 'app/core/config'; import { defaultCodeOptions, Options, TextMode } from './panelcfg.gen'; export interface Props extends PanelProps<Options> {} export function TextPanel(props: Props) { const styles = useStyles2(getStyles); const [processed, setProcessed] = useState<Options>({ mode: props.options.mode, content: processContent(props.options, props.replaceVariables, config.disableSanitizeHtml), }); useDebounce( () => { const { options, replaceVariables } = props; const content = processContent(options, replaceVariables, config.disableSanitizeHtml); if (content !== processed.content || options.mode !== processed.mode) { setProcessed({ mode: options.mode, content, }); } }, 100, [props] ); if (processed.mode === TextMode.Code) { const code = props.options.code ?? defaultCodeOptions; return ( <CodeEditor key={`${code.showLineNumbers}/${code.showMiniMap}`} // will reinit-on change value={processed.content} language={code.language ?? defaultCodeOptions.language!} width={props.width} height={props.height} containerStyles={styles.codeEditorContainer} showMiniMap={code.showMiniMap} showLineNumbers={code.showLineNumbers} readOnly={true} // future /> ); } return ( <div className={styles.containStrict}> <ScrollContainer minHeight="100%"> <DangerouslySetHtmlContent allowRerender html={processed.content} className={cx('markdown-html', styles.markdownHtml)} data-testid="TextPanel-converted-content" /> </ScrollContainer> </div> ); } function processContent(options: Options, interpolate: InterpolateFunction, disableSanitizeHtml: boolean): string { let { mode, content } = options; // Variables must be interpolated before content is converted to markdown so using variables // in URLs work properly content = interpolate(content, {}, options.code?.language === 'json' ? 'json' : 'html'); if (!content) { return ' '; } switch (mode) { case TextMode.Code: break; // nothing case TextMode.HTML: if (!disableSanitizeHtml) { content = textUtil.sanitizeTextPanelContent(content); } break; case TextMode.Markdown: default: // default to markdown content = renderTextPanelMarkdown(content, { noSanitize: disableSanitizeHtml, }); } return content; } const getStyles = (theme: GrafanaTheme2) => ({ codeEditorContainer: css({ '.monaco-editor .margin, .monaco-editor-background': { backgroundColor: theme.colors.background.primary, }, }), containStrict: css({ contain: 'strict', height: '100%', display: 'flex', }), markdownHtml: css({ height: '100%', }), });
.
Edit
..
Edit
README.md
Edit
TextPanel.test.tsx
Edit
TextPanel.tsx
Edit
TextPanelEditor.tsx
Edit
img
Edit
module.tsx
Edit
panelcfg.cue
Edit
panelcfg.gen.ts
Edit
plugin.json
Edit
textPanelMigrationHandler.test.ts
Edit
textPanelMigrationHandler.ts
Edit