DashboardScene: Panel edit progress (#82288)

This commit is contained in:
Torkel Ödegaard
2024-02-12 11:45:34 +01:00
committed by GitHub
parent 18ee8f7583
commit 7f7f1b17e6
5 changed files with 108 additions and 92 deletions
@@ -177,6 +177,7 @@ function getStyles(theme: GrafanaTheme2) {
}),
tabsBar: css({
flexShrink: 0,
paddingLeft: theme.spacing(2),
}),
scroll: css({
background: theme.colors.background.primary,
@@ -116,6 +116,7 @@ export function buildPanelEditScene(panel: VizPanel): PanelEditor {
panelRef: vizPanelMgr.getRef(),
body: new SplitLayout({
direction: 'row',
initialSize: 0.75,
primary: new SplitLayout({
direction: 'column',
$behaviors: [conditionalDataPaneBehavior],
@@ -77,14 +77,13 @@ function getStyles(theme: GrafanaTheme2) {
position: 'relative',
minHeight: 0,
gap: '8px',
marginBottom: theme.spacing(2),
}),
controls: css({
display: 'flex',
flexWrap: 'wrap',
alignItems: 'center',
gap: theme.spacing(1),
padding: theme.spacing(2, 0),
padding: theme.spacing(2),
}),
};
}
@@ -1,10 +1,10 @@
import { css } from '@emotion/css';
import React, { useMemo, useState } from 'react';
import React, { useMemo } from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { SceneComponentProps, SceneObjectBase, SceneObjectState, sceneGraph } from '@grafana/scenes';
import { ButtonGroup, FilterInput, RadioButtonGroup, ToolbarButton, useStyles2 } from '@grafana/ui';
import { Box, ButtonGroup, FilterInput, RadioButtonGroup, ToolbarButton, useStyles2 } from '@grafana/ui';
import { OptionFilter, RenderSearchHits } from 'app/features/dashboard/components/PanelEditor/OptionsPaneOptions';
import { getFieldOverrideCategories } from 'app/features/dashboard/components/PanelEditor/getFieldOverrideElements';
import { getPanelFrameCategory2 } from 'app/features/dashboard/components/PanelEditor/getPanelFrameOptions';
@@ -14,18 +14,40 @@ import { getAllPanelPluginMeta } from 'app/features/panel/state/util';
import { PanelVizTypePicker } from './PanelVizTypePicker';
import { VizPanelManager } from './VizPanelManager';
export interface PanelOptionsPaneState extends SceneObjectState {}
export interface PanelOptionsPaneState extends SceneObjectState {
isVizPickerOpen?: boolean;
searchQuery: string;
listMode: OptionFilter;
}
export class PanelOptionsPane extends SceneObjectBase<PanelOptionsPaneState> {
public panelManager: VizPanelManager;
public constructor(panelMgr: VizPanelManager) {
super({});
super({
searchQuery: '',
listMode: OptionFilter.All,
});
this.panelManager = panelMgr;
}
onToggleVizPicker = () => {
this.setState({ isVizPickerOpen: !this.state.isVizPickerOpen });
};
onSetSearchQuery = (searchQuery: string) => {
this.setState({ searchQuery });
};
onSetListMode = (listMode: OptionFilter) => {
this.setState({ listMode });
};
onCollapsePane = () => {};
static Component = ({ model }: SceneComponentProps<PanelOptionsPane>) => {
const { isVizPickerOpen, searchQuery, listMode } = model.useState();
const { panelManager } = model;
const { panel } = panelManager.state;
const dataObject = sceneGraph.getData(panel);
@@ -33,10 +55,7 @@ export class PanelOptionsPane extends SceneObjectBase<PanelOptionsPaneState> {
const dataWithFieldConfig = panel.applyFieldConfig(rawData.data!);
const { pluginId, options, fieldConfig } = panel.useState();
const styles = useStyles2(getStyles);
const [isVizPickerOpen, setVizPickerOpen] = useState(true);
const [searchQuery, setSearchQuery] = useState('');
const panelFrameOptions = useMemo(() => getPanelFrameCategory2(panel), [panel]);
const [listMode, setListMode] = useState(OptionFilter.All);
const visualizationOptions = useMemo(() => {
const plugin = panel.getPlugin();
@@ -105,44 +124,42 @@ export class PanelOptionsPane extends SceneObjectBase<PanelOptionsPaneState> {
}
return (
<div className={styles.wrapper}>
<div className={styles.box}>
{!isVizPickerOpen && (
<VisualizationButton
pluginId={pluginId}
onClick={() => {
setVizPickerOpen(true);
}}
/>
<Box paddingX={1} paddingTop={1}>
<VisualizationButton
pluginId={pluginId}
onOpen={model.onToggleVizPicker}
isOpen={isVizPickerOpen}
onTogglePane={model.onCollapsePane}
/>
</Box>
)}
{isVizPickerOpen && <PanelVizTypePicker panelManager={panelManager} onChange={model.onToggleVizPicker} />}
{!isVizPickerOpen && (
<>
<div className={styles.top}>
<FilterInput
className={styles.searchOptions}
value={searchQuery}
placeholder="Search options"
onChange={model.onSetSearchQuery}
/>
{!isSearching && (
<RadioButtonGroup
options={[
{ label: 'All', value: OptionFilter.All },
{ label: 'Overrides', value: OptionFilter.Overrides },
]}
value={listMode}
onChange={model.onSetListMode}
fullWidth
></RadioButtonGroup>
)}
</div>
<div className={styles.mainBox}>{mainBoxElements}</div>
</>
)}
<div className={styles.box}>
{isVizPickerOpen && (
<PanelVizTypePicker panelManager={panelManager} onChange={() => setVizPickerOpen(false)} />
)}
{!isVizPickerOpen && (
<>
<div className={styles.top}>
<FilterInput
className={styles.searchOptions}
value={searchQuery}
placeholder="Search options"
onChange={setSearchQuery}
/>
{!isSearching && (
<RadioButtonGroup
options={[
{ label: 'All', value: OptionFilter.All },
{ label: 'Overrides', value: OptionFilter.Overrides },
]}
value={listMode}
onChange={setListMode}
fullWidth
></RadioButtonGroup>
)}
</div>
<div className={styles.mainBox}>{mainBoxElements}</div>
</>
)}
</div>
</div>
);
};
@@ -150,34 +167,25 @@ export class PanelOptionsPane extends SceneObjectBase<PanelOptionsPaneState> {
function getStyles(theme: GrafanaTheme2) {
return {
top: css({
display: 'flex',
flexDirection: 'column',
padding: theme.spacing(1),
gap: theme.spacing(1),
border: `1px solid ${theme.colors.border.weak}`,
borderBottom: 'none',
borderTopLeftRadius: theme.shape.radius.default,
background: theme.colors.background.primary,
}),
box: css({
display: 'flex',
flexDirection: 'column',
flexGrow: '1',
background: theme.colors.background.primary,
overflow: 'hidden',
border: `1px solid ${theme.colors.border.weak}`,
borderBottom: 'none',
borderTopLeftRadius: theme.shape.radius.default,
}),
wrapper: css({
top: css({
display: 'flex',
flexDirection: 'column',
flexGrow: '1',
gap: theme.spacing(2),
padding: theme.spacing(1),
gap: theme.spacing(1),
}),
mainBox: css({
flexGrow: 1,
background: theme.colors.background.primary,
border: `1px solid ${theme.components.panel.borderColor}`,
borderTop: 'none',
overflow: 'auto',
}),
searchOptions: css({
@@ -186,24 +194,14 @@ function getStyles(theme: GrafanaTheme2) {
};
}
export const VisualizationButton = ({ pluginId, onClick }: { pluginId: string; onClick: () => void }) => {
// const dispatch = useDispatch();
// const plugin = useSelector(getPanelPluginWithFallback(panel.type));
// const isPanelOptionsVisible = useSelector((state) => state.panelEditor.ui.isPanelOptionsVisible);
// const isVizPickerOpen = useSelector((state) => state.panelEditor.isVizPickerOpen);
// const onToggleOpen = () => {
// dispatch(toggleVizPicker(!isVizPickerOpen));
// };
// const onToggleOptionsPane = () => {
// dispatch(updatePanelEditorUIState({ isPanelOptionsVisible: !isPanelOptionsVisible }));
// };
// if (!plugin) {
// return null;
// }
interface VisualizationButtonProps {
pluginId: string;
onOpen: () => void;
isOpen?: boolean;
onTogglePane?: () => void;
}
export function VisualizationButton({ pluginId, onOpen, isOpen, onTogglePane }: VisualizationButtonProps) {
const styles = useStyles2(getVizButtonStyles);
const pluginMeta = useMemo(() => getAllPanelPluginMeta().filter((p) => p.id === pluginId)[0], [pluginId]);
@@ -215,7 +213,7 @@ export const VisualizationButton = ({ pluginId, onClick }: { pluginId: string; o
tooltip="Click to change visualization"
imgSrc={pluginMeta.info.logos.small}
// isOpen={isVizPickerOpen}
onClick={onClick}
onClick={onOpen}
data-testid={selectors.components.PanelEditor.toggleVizPicker}
aria-label="Change Visualization"
variant="canvas"
@@ -223,25 +221,24 @@ export const VisualizationButton = ({ pluginId, onClick }: { pluginId: string; o
>
{pluginMeta.name}
</ToolbarButton>
{/* <ToolbarButton
tooltip={isPanelOptionsVisible ? 'Close options pane' : 'Show options pane'}
icon={isPanelOptionsVisible ? 'angle-right' : 'angle-left'}
onClick={onToggleOptionsPane}
<ToolbarButton
tooltip={isOpen ? 'Close options pane' : 'Show options pane'}
icon={isOpen ? 'angle-right' : 'angle-left'}
onClick={onTogglePane}
variant="canvas"
data-testid={selectors.components.PanelEditor.toggleVizOptions}
aria-label={isPanelOptionsVisible ? 'Close options pane' : 'Show options pane'}
/> */}
aria-label={isOpen ? 'Close options pane' : 'Show options pane'}
/>
</ButtonGroup>
</div>
);
};
}
function getVizButtonStyles(theme: GrafanaTheme2) {
return {
wrapper: css({
display: 'flex',
flexDirection: 'column',
paddingRight: theme.spacing(2),
}),
vizButton: css({
textAlign: 'left',
@@ -1,9 +1,11 @@
import { css } from '@emotion/css';
import React from 'react';
import {
DataSourceApi,
DataSourceInstanceSettings,
FieldConfigSource,
GrafanaTheme2,
PanelModel,
filterFieldConfigOverrides,
isStandardFieldProp,
@@ -23,6 +25,7 @@ import {
SceneDataTransformer,
} from '@grafana/scenes';
import { DataQuery, DataTransformerConfig } from '@grafana/schema';
import { useStyles2 } from '@grafana/ui';
import { getPluginVersion } from 'app/features/dashboard/state/PanelModel';
import { getLastUsedDatasourceFromStorage } from 'app/features/dashboard/utils/dashboard';
import { storeLastUsedDataSourceInLocalStorage } from 'app/features/datasources/components/picker/utils';
@@ -41,12 +44,6 @@ interface VizPanelManagerState extends SceneObjectState {
// VizPanelManager serves as an API to manipulate VizPanel state from the outside. It allows panel type, options and data manipulation.
export class VizPanelManager extends SceneObjectBase<VizPanelManagerState> {
public static Component = ({ model }: SceneComponentProps<VizPanelManager>) => {
const { panel } = model.useState();
return <panel.Component model={panel} />;
};
private _cachedPluginOptions: Record<
string,
{ options: DeepPartial<{}>; fieldConfig: FieldConfigSource<DeepPartial<{}>> } | undefined
@@ -278,4 +275,25 @@ export class VizPanelManager extends SceneObjectBase<VizPanelManagerState> {
get panelData(): SceneDataProvider {
return this.state.panel.state.$data!;
}
public static Component = ({ model }: SceneComponentProps<VizPanelManager>) => {
const { panel } = model.useState();
const styles = useStyles2(getStyles);
return (
<div className={styles.wrapper}>
<panel.Component model={panel} />
</div>
);
};
}
function getStyles(theme: GrafanaTheme2) {
return {
wrapper: css({
height: '100%',
width: '100%',
paddingLeft: theme.spacing(2),
}),
};
}