Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dashboards/src/components/GridLayout/GridItemContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export function GridItemContent(props: GridItemContentProps): ReactElement {
return {
kind: query.spec.plugin.kind,
spec: query.spec.plugin.spec,
hidden: query.spec.hidden,
};
});

Expand Down
20 changes: 15 additions & 5 deletions dashboards/src/components/Panel/PanelContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -32,6 +32,19 @@ 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?.spec.hidden ? [{ data: q.data, definition: q.definition }] : []
),
[queryResults]
);

const areAllQueriesHidden = useMemo(
() => queryResults.every((q) => q.definition?.spec.hidden ?? false),
[queryResults]
);

// Show fullsize skeleton if the panel plugin is loading.
if (isPanelLoading) {
return (
Expand All @@ -46,10 +59,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 (
<PanelPluginLoader
kind={panelPluginKind}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export function PanelQueriesSharedControls({
return {
kind: query.spec.plugin.kind,
spec: query.spec.plugin.spec,
hidden: query.spec.hidden,
};
}) ?? []
);
Expand All @@ -67,11 +68,35 @@ export function PanelQueriesSharedControls({
newDefinitions[index] = {
kind: newDef.spec.plugin.kind,
spec: newDef.spec.plugin.spec,
hidden: newDef.spec.hidden,
};
return newDefinitions;
});
}, []);

const handleQueriesChange = useCallback(
(queries: QueryDefinition[]) => {
const didChangeVisibility = queries.some(
(query) => query.spec.hidden !== previewDefinition.find((p) => p.kind === query.spec.plugin.kind)?.spec.hidden
);

if (didChangeVisibility) {
setPreviewDefinition(
queries.map((query) => {
return {
kind: query.spec.plugin.kind,
spec: query.spec.plugin.spec,
hidden: query.spec.hidden,
};
})
);
}

onQueriesChange(queries);
},
[onQueriesChange, previewDefinition]
);

return (
<DataQueriesProvider definitions={previewDefinition} options={{ suggestedStepMs, ...pluginQueryOptions }}>
<Grid item xs={12}>
Expand All @@ -88,7 +113,7 @@ export function PanelQueriesSharedControls({
control={control}
panelDefinition={panelDefinition}
onJSONChange={onJSONChange}
onQueriesChange={onQueriesChange}
onQueriesChange={handleQueriesChange}
onQueryRun={handleRunQuery}
onPluginSpecChange={onPluginSpecChange}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -138,6 +138,20 @@ export const MultiQueryEditor = forwardRef<PluginEditorRef, MultiQueryEditorProp
});
};

const handleVisibilityToggle = useCallback(
(index: number, isHidden: boolean) => {
onChange(
produce(queries, (draft) => {
const entry = draft?.[index];
if (entry) {
entry.spec.hidden = !isHidden;
}
})
);
},
[onChange, queries]
);

// show one query input if queries is empty
const queryDefinitions: QueryDefinition[] = queries.length
? queries
Expand All @@ -162,6 +176,8 @@ export const MultiQueryEditor = forwardRef<PluginEditorRef, MultiQueryEditorProp
onQueryRun={handleQueryRun}
onDelete={queries.length > 1 ? handleQueryDelete : undefined}
onCollapseExpand={handleQueryCollapseExpand}
onVisibilityToggle={handleVisibilityToggle}
isHidden={query.spec.hidden}
/>
))}
</Stack>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;
}

/**
Expand All @@ -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
*/

Expand All @@ -61,10 +67,12 @@ export const QueryEditorContainer = forwardRef<PluginEditorRef, QueryEditorConta
queryResult,
filteredQueryPlugins,
isCollapsed,
isHidden,
onDelete,
onChange,
onQueryRun,
onCollapseExpand,
onVisibilityToggle,
} = props;
return (
<Stack key={index} spacing={1}>
Expand All @@ -79,9 +87,16 @@ export const QueryEditorContainer = forwardRef<PluginEditorRef, QueryEditorConta
<IconButton size="small" onClick={() => onCollapseExpand(index)}>
{isCollapsed ? <ChevronRight /> : <ChevronDown />}
</IconButton>
<Typography variant="overline" component="h4">
Query #{index + 1}
</Typography>
<Stack gap={0.5} direction="row" alignItems="center">
<Typography variant="overline" component="h4">
Query #{index + 1}
</Typography>
{isHidden && (
<Typography variant="caption" color="secondary" component="span" fontStyle="italic">
Disabled
</Typography>
)}
</Stack>
</Stack>
<Stack direction="row" alignItems="center">
{queryResult?.isFetching && <CircularProgress aria-label="loading" size="1.125rem" />}
Expand Down Expand Up @@ -117,6 +132,18 @@ export const QueryEditorContainer = forwardRef<PluginEditorRef, QueryEditorConta
</Stack>
</InfoTooltip>
)}
{onVisibilityToggle && (
<Tooltip title={isHidden ? 'Show in chart' : 'Hide from chart'}>
<IconButton
aria-label={isHidden ? 'show query in chart' : 'hide query from chart'}
size="small"
onClick={() => onVisibilityToggle?.(index, isHidden ?? false)}
sx={{ color: isHidden ? 'text.disabled' : 'text.secondary' }}
>
{isHidden ? <EyeOffIcon /> : <EyeIcon />}
</IconButton>
</Tooltip>
)}
{onDelete && (
<IconButton aria-label="delete query" size="small" onClick={() => onDelete && onDelete(index)}>
<DeleteIcon />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export function DataQueriesProvider(props: DataQueriesProviderProps): ReactEleme
kind: type,
spec: {
plugin: definition,
hidden: definition.hidden,
},
};
});
Expand Down
2 changes: 1 addition & 1 deletion plugin-system/src/runtime/DataQueriesProvider/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { useListPluginMetadata } from '../plugin-registry';

export type QueryOptions = Record<string, unknown>;
export interface DataQueriesProviderProps<QueryPluginSpec = UnknownSpec> {
definitions: Array<Definition<QueryPluginSpec>>;
definitions: Array<Definition<QueryPluginSpec> & { hidden?: boolean }>;
children?: ReactNode;
options?: QueryOptions;
queryOptions?: Omit<QueryObserverOptions, 'queryKey'>;
Expand Down