Commit 16f0e754 by Torkel Ödegaard Committed by GitHub

New panel editor (behind feature toggle) (#21097)

* WIP: initial 10min poc of new panel editor

* added queries

* PanelEditor: copy panel model when going into edit mode

* Added option
parent 664cb5f8
...@@ -15,6 +15,7 @@ interface FeatureToggles { ...@@ -15,6 +15,7 @@ interface FeatureToggles {
transformations: boolean; transformations: boolean;
inspect: boolean; inspect: boolean;
expressions: boolean; expressions: boolean;
newEdit: boolean;
} }
export class GrafanaBootConfig { export class GrafanaBootConfig {
datasources: { [str: string]: DataSourceInstanceSettings } = {}; datasources: { [str: string]: DataSourceInstanceSettings } = {};
...@@ -51,6 +52,7 @@ export class GrafanaBootConfig { ...@@ -51,6 +52,7 @@ export class GrafanaBootConfig {
transformations: false, transformations: false,
inspect: false, inspect: false,
expressions: false, expressions: false,
newEdit: false,
}; };
constructor(options: GrafanaBootConfig) { constructor(options: GrafanaBootConfig) {
......
...@@ -123,6 +123,12 @@ export class KeybindingSrv { ...@@ -123,6 +123,12 @@ export class KeybindingSrv {
return; return;
} }
if (search.editPanel) {
delete search.editPanel;
this.$location.search(search);
return;
}
if (search.fullscreen) { if (search.fullscreen) {
appEvents.emit(PanelEvents.panelChangeView, { fullscreen: false, edit: false }); appEvents.emit(PanelEvents.panelChangeView, { fullscreen: false, edit: false });
return; return;
......
import React, { PureComponent } from 'react';
import { css } from 'emotion';
import { GrafanaTheme } from '@grafana/data';
import { stylesFactory } from '@grafana/ui';
import config from 'app/core/config';
import { PanelModel } from '../../state/PanelModel';
import { DashboardModel } from '../../state/DashboardModel';
import { DashboardPanel } from '../../dashgrid/DashboardPanel';
import { QueriesTab } from '../../panel_editor/QueriesTab';
const getStyles = stylesFactory((theme: GrafanaTheme) => ({
wrapper: css`
width: 100%;
height: 100%;
position: fixed;
z-index: ${theme.zIndex.modal};
top: 0;
left: 0;
right: 0;
bottom: 0;
background: ${theme.colors.pageBg};
display: flex;
padding: ${theme.spacing.md};
flex-direction: row;
`,
leftPane: css`
flex-grow: 1;
height: 100%;
`,
rightPane: css`
width: 450px;
height: 100%;
flex-grow: 0;
`,
leftPaneViz: css`
width: 100%;
height: 50%;
`,
leftPaneData: css`
width: 100%;
height: 50%;
padding-top: ${theme.spacing.md};
`,
}));
interface Props {
dashboard: DashboardModel;
panel: PanelModel;
}
interface State {
dirtyPanel?: PanelModel;
}
export class PanelEditor extends PureComponent<Props, State> {
state: State = {};
componentDidMount() {
const { panel } = this.props;
const dirtyPanel = new PanelModel(panel.getSaveModel());
this.setState({ dirtyPanel });
}
render() {
const { dashboard } = this.props;
const { dirtyPanel } = this.state;
const styles = getStyles(config.theme);
if (!dirtyPanel) {
return null;
}
return (
<div className={styles.wrapper}>
<div className={styles.leftPane}>
<div className={styles.leftPaneViz}>
<DashboardPanel
dashboard={dashboard}
panel={dirtyPanel}
isEditing={false}
isFullscreen={false}
isInView={true}
/>
</div>
<div className={styles.leftPaneData}>
<QueriesTab panel={dirtyPanel} dashboard={dashboard} />;
</div>
</div>
<div className={styles.rightPane}>Visualization settings</div>
</div>
);
}
}
export { PanelEditor } from './PanelEditor';
...@@ -12,7 +12,9 @@ import { DashboardGrid } from '../dashgrid/DashboardGrid'; ...@@ -12,7 +12,9 @@ import { DashboardGrid } from '../dashgrid/DashboardGrid';
import { DashNav } from '../components/DashNav'; import { DashNav } from '../components/DashNav';
import { SubMenu } from '../components/SubMenu'; import { SubMenu } from '../components/SubMenu';
import { DashboardSettings } from '../components/DashboardSettings'; import { DashboardSettings } from '../components/DashboardSettings';
import { Alert, CustomScrollbar } from '@grafana/ui'; import { PanelEditor } from '../components/PanelEditor';
import { CustomScrollbar, Alert } from '@grafana/ui';
// Redux // Redux
import { initDashboard } from '../state/initDashboard'; import { initDashboard } from '../state/initDashboard';
import { cleanUpDashboard } from '../state/actions'; import { cleanUpDashboard } from '../state/actions';
...@@ -25,6 +27,7 @@ import { ...@@ -25,6 +27,7 @@ import {
DashboardRouteInfo, DashboardRouteInfo,
StoreState, StoreState,
} from 'app/types'; } from 'app/types';
import { DashboardModel, PanelModel } from 'app/features/dashboard/state'; import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
import { PanelInspector } from '../components/Inspector/PanelInspector'; import { PanelInspector } from '../components/Inspector/PanelInspector';
...@@ -35,6 +38,7 @@ export interface Props { ...@@ -35,6 +38,7 @@ export interface Props {
editview?: string; editview?: string;
urlPanelId?: string; urlPanelId?: string;
urlFolderId?: string; urlFolderId?: string;
urlEditPanel?: string;
inspectPanelId?: string; inspectPanelId?: string;
$scope: any; $scope: any;
$injector: any; $injector: any;
...@@ -248,7 +252,7 @@ export class DashboardPage extends PureComponent<Props, State> { ...@@ -248,7 +252,7 @@ export class DashboardPage extends PureComponent<Props, State> {
} }
render() { render() {
const { dashboard, editview, $injector, isInitSlow, initError, inspectPanelId } = this.props; const { dashboard, editview, $injector, isInitSlow, initError, inspectPanelId, urlEditPanel } = this.props;
const { isSettingsOpening, isEditing, isFullscreen, scrollTop, updateScrollTop } = this.state; const { isSettingsOpening, isEditing, isFullscreen, scrollTop, updateScrollTop } = this.state;
if (!dashboard) { if (!dashboard) {
...@@ -270,6 +274,8 @@ export class DashboardPage extends PureComponent<Props, State> { ...@@ -270,6 +274,8 @@ export class DashboardPage extends PureComponent<Props, State> {
// Find the panel to inspect // Find the panel to inspect
const inspectPanel = inspectPanelId ? dashboard.getPanelById(parseInt(inspectPanelId, 10)) : null; const inspectPanel = inspectPanelId ? dashboard.getPanelById(parseInt(inspectPanelId, 10)) : null;
// find panel being edited
const editPanel = urlEditPanel ? dashboard.getPanelById(parseInt(urlEditPanel, 10)) : null;
// Only trigger render when the scroll has moved by 25 // Only trigger render when the scroll has moved by 25
const approximateScrollTop = Math.round(scrollTop / 25) * 25; const approximateScrollTop = Math.round(scrollTop / 25) * 25;
...@@ -309,6 +315,7 @@ export class DashboardPage extends PureComponent<Props, State> { ...@@ -309,6 +315,7 @@ export class DashboardPage extends PureComponent<Props, State> {
</div> </div>
{inspectPanel && <PanelInspector dashboard={dashboard} panel={inspectPanel} />} {inspectPanel && <PanelInspector dashboard={dashboard} panel={inspectPanel} />}
{editPanel && <PanelEditor dashboard={dashboard} panel={editPanel} />}
</div> </div>
); );
} }
...@@ -320,6 +327,7 @@ export const mapStateToProps = (state: StoreState) => ({ ...@@ -320,6 +327,7 @@ export const mapStateToProps = (state: StoreState) => ({
urlType: state.location.routeParams.type, urlType: state.location.routeParams.type,
editview: state.location.query.editview, editview: state.location.query.editview,
urlPanelId: state.location.query.panelId, urlPanelId: state.location.query.panelId,
urlEditPanel: state.location.query.editPanel,
urlFolderId: state.location.query.folderId, urlFolderId: state.location.query.folderId,
urlFullscreen: !!state.location.query.fullscreen, urlFullscreen: !!state.location.query.fullscreen,
urlEdit: !!state.location.query.edit, urlEdit: !!state.location.query.edit,
......
...@@ -35,6 +35,7 @@ const notPersistedProperties: { [str: string]: boolean } = { ...@@ -35,6 +35,7 @@ const notPersistedProperties: { [str: string]: boolean } = {
fullscreen: true, fullscreen: true,
isEditing: true, isEditing: true,
isInView: true, isInView: true,
isNewEdit: true,
hasRefreshed: true, hasRefreshed: true,
cachedPluginOptions: true, cachedPluginOptions: true,
plugin: true, plugin: true,
...@@ -125,6 +126,7 @@ export class PanelModel { ...@@ -125,6 +126,7 @@ export class PanelModel {
fullscreen: boolean; fullscreen: boolean;
isEditing: boolean; isEditing: boolean;
isInView: boolean; isInView: boolean;
isNewEdit: boolean;
hasRefreshed: boolean; hasRefreshed: boolean;
events: Emitter; events: Emitter;
cacheTimeout?: any; cacheTimeout?: any;
......
...@@ -41,6 +41,18 @@ export const getPanelMenu = (dashboard: DashboardModel, panel: PanelModel) => { ...@@ -41,6 +41,18 @@ export const getPanelMenu = (dashboard: DashboardModel, panel: PanelModel) => {
); );
}; };
const onNewEditPanel = (event: React.MouseEvent<any>) => {
event.preventDefault();
store.dispatch(
updateLocation({
query: {
editPanel: panel.id,
},
partial: true,
})
);
};
const onSharePanel = (event: React.MouseEvent<any>) => { const onSharePanel = (event: React.MouseEvent<any>) => {
event.preventDefault(); event.preventDefault();
sharePanel(dashboard, panel); sharePanel(dashboard, panel);
...@@ -119,6 +131,7 @@ export const getPanelMenu = (dashboard: DashboardModel, panel: PanelModel) => { ...@@ -119,6 +131,7 @@ export const getPanelMenu = (dashboard: DashboardModel, panel: PanelModel) => {
onClick: onNavigateToExplore, onClick: onNavigateToExplore,
}); });
} }
if (config.featureToggles.inspect) { if (config.featureToggles.inspect) {
menu.push({ menu.push({
text: 'Inspect', text: 'Inspect',
...@@ -128,6 +141,15 @@ export const getPanelMenu = (dashboard: DashboardModel, panel: PanelModel) => { ...@@ -128,6 +141,15 @@ export const getPanelMenu = (dashboard: DashboardModel, panel: PanelModel) => {
}); });
} }
if (config.featureToggles.newEdit) {
menu.push({
text: 'New edit',
iconClassName: 'gicon gicon-editor',
onClick: onNewEditPanel,
shortcut: 'p i',
});
}
const subMenu: PanelMenuItem[] = []; const subMenu: PanelMenuItem[] = [];
if (!panel.fullscreen && dashboard.meta.canEdit) { if (!panel.fullscreen && dashboard.meta.canEdit) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment