From 26943747db345ca4f4c11cc551f9ddc72c84831f Mon Sep 17 00:00:00 2001 From: OrReuben Date: Mon, 16 Feb 2026 14:24:34 +0200 Subject: [PATCH 1/4] Up Signed-off-by: OrReuben --- .../components/GridLayout/GridItemContent.tsx | 1 + .../src/components/Panel/PanelContent.tsx | 17 ++++++++---- .../PanelQueriesSharedControls.tsx | 27 ++++++++++++++++++- .../MultiQueryEditor/MultiQueryEditor.tsx | 18 ++++++++++++- .../MultiQueryEditor/QueryEditorContainer.tsx | 22 ++++++++++++++- .../DataQueriesProvider.tsx | 1 + .../src/runtime/DataQueriesProvider/model.ts | 2 +- 7 files changed, 79 insertions(+), 9 deletions(-) diff --git a/dashboards/src/components/GridLayout/GridItemContent.tsx b/dashboards/src/components/GridLayout/GridItemContent.tsx index 86d5a5d..7196d82 100644 --- a/dashboards/src/components/GridLayout/GridItemContent.tsx +++ b/dashboards/src/components/GridLayout/GridItemContent.tsx @@ -89,6 +89,7 @@ export function GridItemContent(props: GridItemContentProps): ReactElement { return { kind: query.spec.plugin.kind, spec: query.spec.plugin.spec, + hidden: query.hidden, }; }); diff --git a/dashboards/src/components/Panel/PanelContent.tsx b/dashboards/src/components/Panel/PanelContent.tsx index 4ac622d..b804e58 100644 --- a/dashboards/src/components/Panel/PanelContent.tsx +++ b/dashboards/src/components/Panel/PanelContent.tsx @@ -13,7 +13,7 @@ import { usePlugin, PanelProps, QueryData, PanelPlugin } from '@perses-dev/plugin-system'; import { UnknownSpec, PanelDefinition, QueryDataType } from '@perses-dev/core'; -import { ReactElement } from 'react'; +import { ReactElement, useMemo } from 'react'; import { LoadingOverlay } from '@perses-dev/components'; import { Skeleton } from '@mui/material'; import { PanelPluginLoader } from './PanelPluginLoader'; @@ -32,6 +32,16 @@ export function PanelContent(props: PanelContentProps): ReactElement { const { panelPluginKind, definition, queryResults, spec, contentDimensions } = props; const { data: plugin, isLoading: isPanelLoading } = usePlugin('Panel', panelPluginKind, { useErrorBoundary: true }); + const queryResultsWithData = useMemo( + () => + queryResults.flatMap((q) => + q.data && !q.definition?.hidden ? [{ data: q.data, definition: q.definition }] : [] + ), + [queryResults] + ); + + const areAllQueriesHidden = useMemo(() => queryResults.every((q) => q.definition?.hidden ?? false), [queryResults]); + // Show fullsize skeleton if the panel plugin is loading. if (isPanelLoading) { return ( @@ -46,10 +56,7 @@ export function PanelContent(props: PanelContentProps): ReactElement { // Render the panel if any query has data, or the panel doesn't have a query attached (for example MarkdownPanel). // Loading indicator or errors of other queries are shown in the panel header. - const queryResultsWithData = queryResults.flatMap((q) => - q.data ? [{ data: q.data, definition: q.definition }] : [] - ); - if (queryResultsWithData.length > 0 || queryResults.length === 0) { + if (queryResultsWithData.length > 0 || queryResults.length === 0 || areAllQueriesHidden) { return ( { + const didChangeVisibility = queries.some( + (query) => query.hidden !== previewDefinition.find((p) => p.kind === query.spec.plugin.kind)?.hidden + ); + + if (didChangeVisibility) { + setPreviewDefinition( + queries.map((query) => { + return { + kind: query.spec.plugin.kind, + spec: query.spec.plugin.spec, + hidden: query.hidden, + }; + }) + ); + } + + onQueriesChange(queries); + }, + [onQueriesChange, previewDefinition] + ); + return ( @@ -88,7 +113,7 @@ export function PanelQueriesSharedControls({ control={control} panelDefinition={panelDefinition} onJSONChange={onJSONChange} - onQueriesChange={onQueriesChange} + onQueriesChange={handleQueriesChange} onQueryRun={handleRunQuery} onPluginSpecChange={onPluginSpecChange} /> diff --git a/plugin-system/src/components/MultiQueryEditor/MultiQueryEditor.tsx b/plugin-system/src/components/MultiQueryEditor/MultiQueryEditor.tsx index d2ea8e9..64c1f8e 100644 --- a/plugin-system/src/components/MultiQueryEditor/MultiQueryEditor.tsx +++ b/plugin-system/src/components/MultiQueryEditor/MultiQueryEditor.tsx @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { forwardRef, ReactElement, useState } from 'react'; +import { forwardRef, ReactElement, useCallback, useState } from 'react'; import { produce } from 'immer'; import { Button, Stack } from '@mui/material'; import AddIcon from 'mdi-material-ui/Plus'; @@ -138,6 +138,20 @@ export const MultiQueryEditor = forwardRef { + onChange( + produce(queries, (draft) => { + const entry = draft?.[index]; + if (entry) { + entry.hidden = !isHidden; + } + }) + ); + }, + [onChange, queries] + ); + // show one query input if queries is empty const queryDefinitions: QueryDefinition[] = queries.length ? queries @@ -162,6 +176,8 @@ export const MultiQueryEditor = forwardRef 1 ? handleQueryDelete : undefined} onCollapseExpand={handleQueryCollapseExpand} + onVisibilityToggle={handleVisibilityToggle} + isHidden={query.hidden} /> ))} diff --git a/plugin-system/src/components/MultiQueryEditor/QueryEditorContainer.tsx b/plugin-system/src/components/MultiQueryEditor/QueryEditorContainer.tsx index 013d875..4f2a45e 100644 --- a/plugin-system/src/components/MultiQueryEditor/QueryEditorContainer.tsx +++ b/plugin-system/src/components/MultiQueryEditor/QueryEditorContainer.tsx @@ -13,10 +13,12 @@ import { produce } from 'immer'; import { QueryDefinition, QueryPluginType } from '@perses-dev/core'; -import { Stack, IconButton, Typography, BoxProps, Box, CircularProgress } from '@mui/material'; +import { Stack, IconButton, Typography, BoxProps, Box, CircularProgress, Tooltip } from '@mui/material'; import DeleteIcon from 'mdi-material-ui/DeleteOutline'; import ChevronDown from 'mdi-material-ui/ChevronDown'; import ChevronRight from 'mdi-material-ui/ChevronRight'; +import EyeIcon from 'mdi-material-ui/Eye'; +import EyeOffIcon from 'mdi-material-ui/EyeOff'; import { forwardRef, ReactElement } from 'react'; import AlertIcon from 'mdi-material-ui/Alert'; import { InfoTooltip } from '@perses-dev/components'; @@ -36,7 +38,9 @@ interface QueryEditorContainerProps { onQueryRun: (index: number, query: QueryDefinition) => void; onCollapseExpand: (index: number) => void; isCollapsed?: boolean; + isHidden?: boolean; onDelete?: (index: number) => void; + onVisibilityToggle?: (index: number, isHidden: boolean) => void; } /** @@ -46,9 +50,11 @@ interface QueryEditorContainerProps { * @param index the index of the query in the list * @param query the query definition * @param isCollapsed whether the query editor is collapsed or not + * @param isHidden whether the query is hidden or not * @param onDelete callback when the query is deleted * @param onChange callback when the query is changed * @param onCollapseExpand callback when the query is collapsed or expanded + * @param onVisibilityToggle callback when the query is hidden or shown * @constructor */ @@ -61,10 +67,12 @@ export const QueryEditorContainer = forwardRef @@ -117,6 +125,18 @@ export const QueryEditorContainer = forwardRef )} + {onVisibilityToggle && ( + + onVisibilityToggle?.(index, isHidden ?? false)} + sx={{ color: isHidden ? 'text.disabled' : 'text.secondary' }} + > + {isHidden ? : } + + + )} {onDelete && ( onDelete && onDelete(index)}> diff --git a/plugin-system/src/runtime/DataQueriesProvider/DataQueriesProvider.tsx b/plugin-system/src/runtime/DataQueriesProvider/DataQueriesProvider.tsx index 97a5ed2..de2b80a 100644 --- a/plugin-system/src/runtime/DataQueriesProvider/DataQueriesProvider.tsx +++ b/plugin-system/src/runtime/DataQueriesProvider/DataQueriesProvider.tsx @@ -74,6 +74,7 @@ export function DataQueriesProvider(props: DataQueriesProviderProps): ReactEleme spec: { plugin: definition, }, + hidden: definition.hidden, }; }); diff --git a/plugin-system/src/runtime/DataQueriesProvider/model.ts b/plugin-system/src/runtime/DataQueriesProvider/model.ts index 8e94759..bc7d971 100644 --- a/plugin-system/src/runtime/DataQueriesProvider/model.ts +++ b/plugin-system/src/runtime/DataQueriesProvider/model.ts @@ -18,7 +18,7 @@ import { useListPluginMetadata } from '../plugin-registry'; export type QueryOptions = Record; export interface DataQueriesProviderProps { - definitions: Array>; + definitions: Array & { hidden?: boolean }>; children?: ReactNode; options?: QueryOptions; queryOptions?: Omit; From 7284b2f3358127b2b6d80ae307855e574b92cddc Mon Sep 17 00:00:00 2001 From: OrReuben Date: Mon, 16 Feb 2026 14:41:57 +0200 Subject: [PATCH 2/4] Up Signed-off-by: OrReuben --- .../MultiQueryEditor/QueryEditorContainer.tsx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/plugin-system/src/components/MultiQueryEditor/QueryEditorContainer.tsx b/plugin-system/src/components/MultiQueryEditor/QueryEditorContainer.tsx index 4f2a45e..d13d167 100644 --- a/plugin-system/src/components/MultiQueryEditor/QueryEditorContainer.tsx +++ b/plugin-system/src/components/MultiQueryEditor/QueryEditorContainer.tsx @@ -87,9 +87,16 @@ export const QueryEditorContainer = forwardRef onCollapseExpand(index)}> {isCollapsed ? : } - - Query #{index + 1} - + + + Query #{index + 1} + + {query.hidden && ( + + Disabled + + )} + {queryResult?.isFetching && } From 44b02036beadbf9d62c5f002edbee8f8f2ec2e0e Mon Sep 17 00:00:00 2001 From: OrReuben Date: Tue, 17 Feb 2026 22:44:28 +0200 Subject: [PATCH 3/4] Merge remote-tracking branch 'upstream/main' into add-hidden-query-button Signed-off-by: OrReuben --- cue-test/common/proxy/sql.cue | 25 +++++++++++++++++++++++++ cue/common/proxy/sql.cue | 6 +++++- 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 cue-test/common/proxy/sql.cue diff --git a/cue-test/common/proxy/sql.cue b/cue-test/common/proxy/sql.cue new file mode 100644 index 0000000..3289ba2 --- /dev/null +++ b/cue-test/common/proxy/sql.cue @@ -0,0 +1,25 @@ +// Copyright The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package proxy + +mySQLProxySpec: #baseSQLDatasourceSpec & { + proxy: #SQLProxy & { + kind: "SQLProxy" + spec: { + driver: "postgres" + host: "localhost:5432" + database: "mydb" + } + } +} diff --git a/cue/common/proxy/sql.cue b/cue/common/proxy/sql.cue index e497a53..dca1ce5 100644 --- a/cue/common/proxy/sql.cue +++ b/cue/common/proxy/sql.cue @@ -46,7 +46,7 @@ import ( #SQLProxy: { kind: "SQLProxy" spec: { - driver: "mysql" | "postgres" + driver: "mysql" | "mariadb" | "postgres" // host is the hostname and port of the datasource. It is not the hostname of the proxy. // The Perses server is the proxy, so it needs to know where to redirect the request. host: string @@ -57,7 +57,11 @@ import ( secret?: string // mysql specific driver configurations mysql?: #MySQL + // mariadb specific driver configurations + mariadb?: #MySQL // postgres specific driver configurations postgres?: #Postgres } } + +#baseSQLDatasourceSpec: { driver: "mysql" | "mariadb" | "postgres", host: string, database: string }| { proxy: #SQLProxy } \ No newline at end of file From 414ca7e81727f675b5ac457a4f499aa87b640de1 Mon Sep 17 00:00:00 2001 From: OrReuben Date: Tue, 17 Feb 2026 22:56:45 +0200 Subject: [PATCH 4/4] move hidden to spec Signed-off-by: OrReuben --- dashboards/src/components/GridLayout/GridItemContent.tsx | 2 +- dashboards/src/components/Panel/PanelContent.tsx | 7 +++++-- .../components/PanelDrawer/PanelQueriesSharedControls.tsx | 8 ++++---- .../src/components/MultiQueryEditor/MultiQueryEditor.tsx | 4 ++-- .../components/MultiQueryEditor/QueryEditorContainer.tsx | 2 +- .../runtime/DataQueriesProvider/DataQueriesProvider.tsx | 2 +- 6 files changed, 14 insertions(+), 11 deletions(-) diff --git a/dashboards/src/components/GridLayout/GridItemContent.tsx b/dashboards/src/components/GridLayout/GridItemContent.tsx index 7196d82..b6c62aa 100644 --- a/dashboards/src/components/GridLayout/GridItemContent.tsx +++ b/dashboards/src/components/GridLayout/GridItemContent.tsx @@ -89,7 +89,7 @@ export function GridItemContent(props: GridItemContentProps): ReactElement { return { kind: query.spec.plugin.kind, spec: query.spec.plugin.spec, - hidden: query.hidden, + hidden: query.spec.hidden, }; }); diff --git a/dashboards/src/components/Panel/PanelContent.tsx b/dashboards/src/components/Panel/PanelContent.tsx index b804e58..36a10a1 100644 --- a/dashboards/src/components/Panel/PanelContent.tsx +++ b/dashboards/src/components/Panel/PanelContent.tsx @@ -35,12 +35,15 @@ export function PanelContent(props: PanelContentProps): ReactElement { const queryResultsWithData = useMemo( () => queryResults.flatMap((q) => - q.data && !q.definition?.hidden ? [{ data: q.data, definition: q.definition }] : [] + q.data && !q.definition?.spec.hidden ? [{ data: q.data, definition: q.definition }] : [] ), [queryResults] ); - const areAllQueriesHidden = useMemo(() => queryResults.every((q) => q.definition?.hidden ?? false), [queryResults]); + const areAllQueriesHidden = useMemo( + () => queryResults.every((q) => q.definition?.spec.hidden ?? false), + [queryResults] + ); // Show fullsize skeleton if the panel plugin is loading. if (isPanelLoading) { diff --git a/dashboards/src/components/PanelDrawer/PanelQueriesSharedControls.tsx b/dashboards/src/components/PanelDrawer/PanelQueriesSharedControls.tsx index 157da3b..0021ec3 100644 --- a/dashboards/src/components/PanelDrawer/PanelQueriesSharedControls.tsx +++ b/dashboards/src/components/PanelDrawer/PanelQueriesSharedControls.tsx @@ -57,7 +57,7 @@ export function PanelQueriesSharedControls({ return { kind: query.spec.plugin.kind, spec: query.spec.plugin.spec, - hidden: query.hidden, + hidden: query.spec.hidden, }; }) ?? [] ); @@ -68,7 +68,7 @@ export function PanelQueriesSharedControls({ newDefinitions[index] = { kind: newDef.spec.plugin.kind, spec: newDef.spec.plugin.spec, - hidden: newDef.hidden, + hidden: newDef.spec.hidden, }; return newDefinitions; }); @@ -77,7 +77,7 @@ export function PanelQueriesSharedControls({ const handleQueriesChange = useCallback( (queries: QueryDefinition[]) => { const didChangeVisibility = queries.some( - (query) => query.hidden !== previewDefinition.find((p) => p.kind === query.spec.plugin.kind)?.hidden + (query) => query.spec.hidden !== previewDefinition.find((p) => p.kind === query.spec.plugin.kind)?.spec.hidden ); if (didChangeVisibility) { @@ -86,7 +86,7 @@ export function PanelQueriesSharedControls({ return { kind: query.spec.plugin.kind, spec: query.spec.plugin.spec, - hidden: query.hidden, + hidden: query.spec.hidden, }; }) ); diff --git a/plugin-system/src/components/MultiQueryEditor/MultiQueryEditor.tsx b/plugin-system/src/components/MultiQueryEditor/MultiQueryEditor.tsx index 64c1f8e..fbec56d 100644 --- a/plugin-system/src/components/MultiQueryEditor/MultiQueryEditor.tsx +++ b/plugin-system/src/components/MultiQueryEditor/MultiQueryEditor.tsx @@ -144,7 +144,7 @@ export const MultiQueryEditor = forwardRef { const entry = draft?.[index]; if (entry) { - entry.hidden = !isHidden; + entry.spec.hidden = !isHidden; } }) ); @@ -177,7 +177,7 @@ export const MultiQueryEditor = forwardRef 1 ? handleQueryDelete : undefined} onCollapseExpand={handleQueryCollapseExpand} onVisibilityToggle={handleVisibilityToggle} - isHidden={query.hidden} + isHidden={query.spec.hidden} /> ))} diff --git a/plugin-system/src/components/MultiQueryEditor/QueryEditorContainer.tsx b/plugin-system/src/components/MultiQueryEditor/QueryEditorContainer.tsx index d13d167..56cdf89 100644 --- a/plugin-system/src/components/MultiQueryEditor/QueryEditorContainer.tsx +++ b/plugin-system/src/components/MultiQueryEditor/QueryEditorContainer.tsx @@ -91,7 +91,7 @@ export const QueryEditorContainer = forwardRef Query #{index + 1} - {query.hidden && ( + {isHidden && ( Disabled diff --git a/plugin-system/src/runtime/DataQueriesProvider/DataQueriesProvider.tsx b/plugin-system/src/runtime/DataQueriesProvider/DataQueriesProvider.tsx index de2b80a..9838a15 100644 --- a/plugin-system/src/runtime/DataQueriesProvider/DataQueriesProvider.tsx +++ b/plugin-system/src/runtime/DataQueriesProvider/DataQueriesProvider.tsx @@ -73,8 +73,8 @@ export function DataQueriesProvider(props: DataQueriesProviderProps): ReactEleme kind: type, spec: { plugin: definition, + hidden: definition.hidden, }, - hidden: definition.hidden, }; });