From 47f82a0c16e06b182693754bd85edcef3178f5f2 Mon Sep 17 00:00:00 2001 From: Sam Jewell <2903904+samjewell@users.noreply.github.com> Date: Wed, 5 Mar 2025 15:52:07 +0000 Subject: [PATCH] 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 --- .../expressions/components/SqlExpr.tsx | 45 +++++++++++++++++-- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/public/app/features/expressions/components/SqlExpr.tsx b/public/app/features/expressions/components/SqlExpr.tsx index 5b8d6c0a424..93fad1699b7 100644 --- a/public/app/features/expressions/components/SqlExpr.tsx +++ b/public/app/features/expressions/components/SqlExpr.tsx @@ -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>; 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(null); + const [dimensions, setDimensions] = useState({ height: 0 }); const onEditorChange = (expression: string) => { onChange({ @@ -23,5 +30,37 @@ export const SqlExpr = ({ onChange, refIds, query }: Props) => { }); }; - return ; + // 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 ( +
+ +
+ ); }; + +const getStyles = () => ({ + editorContainer: css({ + height: '240px', + resize: 'vertical', + overflow: 'auto', + minHeight: '100px', + }), +});