SQL Expressions: Resizable code-editor (#101407)

* Resizeable SQL expressions text-area

Generated by an LLM for me - we'll see. It expands quite large on first
page-load

* Switch to useLayoutEffect to avoid visual flashing

* Get the LLM to rewrite the approach, inspired by InfluxDB

The Influx DB text-area is also resizable vertically, but that one isn't
a Monaco editor (we need to tell Monaco to update its own size when the
outer div is resized), so this is necessarily a little more complex than
Influx. But still this approach looks simpler: The Javascript here is
shorter

* Start at 240px, to match the current default size

Question: Is there a better approach to achieve this?

* Don't clip the bottom border of the Monaco editor

* Fix linting errors
This commit is contained in:
Sam Jewell
2025-03-05 15:52:07 +00:00
committed by GitHub
parent 3bdc9d1e19
commit 47f82a0c16
@@ -1,10 +1,15 @@
import { useMemo } from 'react';
import { css } from '@emotion/css';
import { useMemo, useRef, useEffect, useState } from 'react';
import { SelectableValue } from '@grafana/data';
import { SQLEditor } from '@grafana/plugin-ui';
import { useStyles2 } from '@grafana/ui';
import { ExpressionQuery } from '../types';
// Account for Monaco editor's border to prevent clipping
const EDITOR_BORDER_ADJUSTMENT = 2; // 1px border on top and bottom
interface Props {
refIds: Array<SelectableValue<string>>;
query: ExpressionQuery;
@@ -13,8 +18,10 @@ interface Props {
export const SqlExpr = ({ onChange, refIds, query }: Props) => {
const vars = useMemo(() => refIds.map((v) => v.value!), [refIds]);
const initialQuery = `select * from ${vars[0]} limit 1`;
const styles = useStyles2(getStyles);
const containerRef = useRef<HTMLDivElement>(null);
const [dimensions, setDimensions] = useState({ height: 0 });
const onEditorChange = (expression: string) => {
onChange({
@@ -23,5 +30,37 @@ export const SqlExpr = ({ onChange, refIds, query }: Props) => {
});
};
return <SQLEditor query={query.expression || initialQuery} onChange={onEditorChange}></SQLEditor>;
// Set up resize observer to handle container resizing
useEffect(() => {
if (!containerRef.current) {
return;
}
const resizeObserver = new ResizeObserver((entries) => {
const { height } = entries[0].contentRect;
setDimensions({ height });
});
resizeObserver.observe(containerRef.current);
return () => resizeObserver.disconnect();
}, []);
return (
<div ref={containerRef} className={styles.editorContainer}>
<SQLEditor
query={query.expression || initialQuery}
onChange={onEditorChange}
height={dimensions.height - EDITOR_BORDER_ADJUSTMENT}
/>
</div>
);
};
const getStyles = () => ({
editorContainer: css({
height: '240px',
resize: 'vertical',
overflow: 'auto',
minHeight: '100px',
}),
});