From cd30742616d027b7576ed6ca34fc050eee2c52d6 Mon Sep 17 00:00:00 2001 From: Sam Jewell <2903904+samjewell@users.noreply.github.com> Date: Tue, 1 Apr 2025 16:49:39 +0100 Subject: [PATCH] SQL Expressions: Mount front-end component right first time (#103087) * Call onChange method once on render so we have access to the value on init --------- Co-authored-by: Tom Ratcliffe --- .../expressions/components/SqlExpr.test.tsx | 50 +++++++++++++++++++ .../expressions/components/SqlExpr.tsx | 9 ++++ 2 files changed, 59 insertions(+) create mode 100644 public/app/features/expressions/components/SqlExpr.test.tsx diff --git a/public/app/features/expressions/components/SqlExpr.test.tsx b/public/app/features/expressions/components/SqlExpr.test.tsx new file mode 100644 index 00000000000..20cd0b8d85f --- /dev/null +++ b/public/app/features/expressions/components/SqlExpr.test.tsx @@ -0,0 +1,50 @@ +import { render } from '@testing-library/react'; + +import { ExpressionQuery } from '../types'; + +import { SqlExpr } from './SqlExpr'; + +jest.mock('@grafana/ui', () => ({ + ...jest.requireActual('@grafana/ui'), + useStyles2: jest.fn().mockImplementation(() => ({})), +})); + +jest.mock('@grafana/plugin-ui', () => ({ + SQLEditor: () =>
SQL Editor Mock
, +})); + +describe('SqlExpr', () => { + it('initializes new expressions with default query', () => { + const onChange = jest.fn(); + const refIds = [{ value: 'A' }]; + const query = { refId: 'expr1', type: 'sql', expression: '' } as ExpressionQuery; + + render(); + + // Verify onChange was called + expect(onChange).toHaveBeenCalled(); + + // Verify essential SQL structure without exact string matching + const updatedQuery = onChange.mock.calls[0][0]; + expect(updatedQuery.expression.toUpperCase()).toContain('SELECT'); + }); + + it('preserves existing expressions when mounted', () => { + const onChange = jest.fn(); + const refIds = [{ value: 'A' }]; + const existingExpression = 'SELECT 1 AS foo'; + const query = { refId: 'expr1', type: 'sql', expression: existingExpression } as ExpressionQuery; + + render(); + + // Check if onChange was called + if (onChange.mock.calls.length > 0) { + // If called, ensure it didn't change the expression value + const updatedQuery = onChange.mock.calls[0][0]; + expect(updatedQuery.expression).toBe(existingExpression); + } + + // The SQLEditor should receive the existing expression + expect(query.expression).toBe(existingExpression); + }); +}); diff --git a/public/app/features/expressions/components/SqlExpr.tsx b/public/app/features/expressions/components/SqlExpr.tsx index 2d7747c8d17..a9a8d9a45cc 100644 --- a/public/app/features/expressions/components/SqlExpr.tsx +++ b/public/app/features/expressions/components/SqlExpr.tsx @@ -60,6 +60,15 @@ LIMIT 10`; return () => resizeObserver.disconnect(); }, []); + useEffect(() => { + // Call the onChange method once so we have access to the initial query in consuming components + // But only if expression is empty + if (!query.expression) { + onEditorChange(initialQuery); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + return (