DashboardScene: Panel edit progress (#82288)
This commit is contained in:
@@ -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),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user