mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
wip: panel-header: More merge conflicts
This commit is contained in:
parent
ca4612af26
commit
6c0c1254fe
@ -115,7 +115,6 @@ export class DashboardPanel extends PureComponent<Props, State> {
|
||||
const { pluginExports } = this.state;
|
||||
const containerClass = this.props.panel.isEditing ? 'panel-editor-container' : 'panel-height-helper';
|
||||
const panelWrapperClass = this.props.panel.isEditing ? 'panel-editor-container__panel' : 'panel-height-helper';
|
||||
|
||||
// this might look strange with these classes that change when edit, but
|
||||
// I want to try to keep markup (parents) for panel the same in edit mode to avoide unmount / new mount of panel
|
||||
return (
|
||||
@ -126,6 +125,7 @@ export class DashboardPanel extends PureComponent<Props, State> {
|
||||
withMenuOptions={pluginExports.withMenuOptions}
|
||||
panel={this.props.panel}
|
||||
dashboard={this.props.dashboard}
|
||||
moduleMenu={pluginExports.moduleMenu}
|
||||
/>
|
||||
</div>
|
||||
{this.props.panel.isEditing && (
|
||||
|
@ -1,11 +1,9 @@
|
||||
// Library
|
||||
import React, { Component } from 'react';
|
||||
|
||||
// Services
|
||||
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
||||
|
||||
// Types
|
||||
import { TimeRange, LoadingState, DataQueryOptions, DataQueryResponse, TimeSeries } from 'app/types';
|
||||
import { DataSourceApi } from 'app/types/series';
|
||||
|
||||
interface RenderProps {
|
||||
loading: LoadingState;
|
||||
@ -13,7 +11,7 @@ interface RenderProps {
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
datasource: string | null;
|
||||
dataSourceApi: DataSourceApi;
|
||||
queries: any[];
|
||||
panelId?: number;
|
||||
dashboardId?: number;
|
||||
@ -21,6 +19,7 @@ export interface Props {
|
||||
timeRange?: TimeRange;
|
||||
refreshCounter: number;
|
||||
children: (r: RenderProps) => JSX.Element;
|
||||
onIssueQueryResponse: any;
|
||||
}
|
||||
|
||||
export interface State {
|
||||
@ -60,13 +59,19 @@ export class DataPanel extends Component<Props, State> {
|
||||
}
|
||||
|
||||
hasPropsChanged(prevProps: Props) {
|
||||
return this.props.refreshCounter !== prevProps.refreshCounter || this.props.isVisible !== prevProps.isVisible;
|
||||
const { refreshCounter, isVisible, dataSourceApi } = this.props;
|
||||
|
||||
return (
|
||||
refreshCounter !== prevProps.refreshCounter ||
|
||||
isVisible !== prevProps.isVisible ||
|
||||
dataSourceApi !== prevProps.dataSourceApi
|
||||
);
|
||||
}
|
||||
|
||||
issueQueries = async () => {
|
||||
const { isVisible, queries, datasource, panelId, dashboardId, timeRange } = this.props;
|
||||
const { isVisible, queries, panelId, dashboardId, timeRange, dataSourceApi } = this.props;
|
||||
|
||||
if (!isVisible) {
|
||||
if (!isVisible || !dataSourceApi) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -78,9 +83,6 @@ export class DataPanel extends Component<Props, State> {
|
||||
this.setState({ loading: LoadingState.Loading });
|
||||
|
||||
try {
|
||||
const dataSourceSrv = getDatasourceSrv();
|
||||
const ds = await dataSourceSrv.get(datasource);
|
||||
|
||||
const queryOptions: DataQueryOptions = {
|
||||
timezone: 'browser',
|
||||
panelId: panelId,
|
||||
@ -96,7 +98,7 @@ export class DataPanel extends Component<Props, State> {
|
||||
};
|
||||
|
||||
console.log('Issuing DataPanel query', queryOptions);
|
||||
const resp = await ds.query(queryOptions);
|
||||
const resp = await dataSourceApi.query(queryOptions);
|
||||
console.log('Issuing DataPanel query Resp', resp);
|
||||
|
||||
this.setState({
|
||||
@ -104,6 +106,8 @@ export class DataPanel extends Component<Props, State> {
|
||||
response: resp,
|
||||
isFirstLoad: false,
|
||||
});
|
||||
|
||||
this.props.onIssueQueryResponse(resp.data);
|
||||
} catch (err) {
|
||||
console.log('Loading error', err);
|
||||
this.setState({ loading: LoadingState.Error, isFirstLoad: false });
|
||||
|
@ -3,43 +3,62 @@ import React, { ComponentClass, PureComponent } from 'react';
|
||||
|
||||
// Services
|
||||
import { getTimeSrv } from '../time_srv';
|
||||
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
||||
|
||||
// Components
|
||||
import { PanelHeader } from './PanelHeader/PanelHeader';
|
||||
import { DataPanel } from './DataPanel';
|
||||
import { PanelHeaderMenu } from './PanelHeader/PanelHeaderMenu';
|
||||
|
||||
// Types
|
||||
import { PanelModel } from '../panel_model';
|
||||
import { DashboardModel } from '../dashboard_model';
|
||||
import { TimeRange, PanelProps } from 'app/types';
|
||||
import { TimeRange, PanelProps, TimeSeries } from 'app/types';
|
||||
import { DataSourceApi } from 'app/types/series';
|
||||
|
||||
export interface PanelChromeProps {
|
||||
panel: PanelModel;
|
||||
dashboard: DashboardModel;
|
||||
component: ComponentClass<PanelProps>;
|
||||
withMenuOptions: any;
|
||||
withMenuOptions?: (c: typeof PanelHeaderMenu, p: PanelModel) => typeof PanelHeaderMenu;
|
||||
moduleMenu?: any;
|
||||
}
|
||||
|
||||
export interface PanelChromeState {
|
||||
refreshCounter: number;
|
||||
renderCounter: number;
|
||||
timeRange?: TimeRange;
|
||||
timeSeries?: TimeSeries[];
|
||||
dataSourceApi?: DataSourceApi;
|
||||
}
|
||||
|
||||
export class PanelChrome extends PureComponent<PanelChromeProps, PanelChromeState> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
refreshCounter: 0,
|
||||
renderCounter: 0,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
async componentDidMount() {
|
||||
const { panel } = this.props;
|
||||
const { datasource } = panel;
|
||||
|
||||
this.props.panel.events.on('refresh', this.onRefresh);
|
||||
this.props.panel.events.on('render', this.onRender);
|
||||
this.props.dashboard.panelInitialized(this.props.panel);
|
||||
|
||||
try {
|
||||
const dataSourceSrv = getDatasourceSrv();
|
||||
const dataSourceApi = await dataSourceSrv.get(datasource);
|
||||
this.setState(prevState => ({
|
||||
...prevState,
|
||||
dataSourceApi,
|
||||
}));
|
||||
} catch (err) {
|
||||
console.log('Datasource loading error', err);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
@ -50,10 +69,11 @@ export class PanelChrome extends PureComponent<PanelChromeProps, PanelChromeStat
|
||||
const timeSrv = getTimeSrv();
|
||||
const timeRange = timeSrv.timeRange();
|
||||
|
||||
this.setState({
|
||||
this.setState(prevState => ({
|
||||
...prevState,
|
||||
refreshCounter: this.state.refreshCounter + 1,
|
||||
timeRange: timeRange,
|
||||
});
|
||||
}));
|
||||
};
|
||||
|
||||
onRender = () => {
|
||||
@ -63,27 +83,50 @@ export class PanelChrome extends PureComponent<PanelChromeProps, PanelChromeStat
|
||||
});
|
||||
};
|
||||
|
||||
onIssueQueryResponse = (timeSeries: any) => {
|
||||
this.setState(prevState => ({
|
||||
...prevState,
|
||||
timeSeries,
|
||||
}));
|
||||
};
|
||||
|
||||
get isVisible() {
|
||||
return !this.props.dashboard.otherPanelInFullscreen(this.props.panel);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { panel, dashboard, withMenuOptions } = this.props;
|
||||
const { datasource, targets } = panel;
|
||||
const { timeRange, renderCounter, refreshCounter } = this.state;
|
||||
const { panel, dashboard, moduleMenu } = this.props;
|
||||
const { refreshCounter, timeRange, dataSourceApi, timeSeries, renderCounter } = this.state;
|
||||
const { targets } = panel;
|
||||
const PanelComponent = this.props.component;
|
||||
console.log('Panel chrome render');
|
||||
// const PanelHeaderMenuComponent: typeof PanelHeaderMenu = withMenuOptions ? withMenuOptions(PanelHeaderMenu, panel) : PanelHeaderMenu;
|
||||
const PanelHeaderMenuComponent = PanelHeaderMenu;
|
||||
const mm = moduleMenu(panel, dataSourceApi, timeSeries);
|
||||
const additionalMenuItems = mm.getAdditionalMenuItems || undefined;
|
||||
const additionalSubMenuItems = mm.getAdditionalSubMenuItems || undefined;
|
||||
|
||||
console.log('panelChrome render');
|
||||
return (
|
||||
<div className="panel-container">
|
||||
<PanelHeader panel={panel} dashboard={dashboard} withMenuOptions={withMenuOptions} />
|
||||
<PanelHeader title={panel.title}>
|
||||
<PanelHeaderMenuComponent
|
||||
panel={panel}
|
||||
dashboard={dashboard}
|
||||
dataSourceApi={dataSourceApi}
|
||||
additionalMenuItems={additionalMenuItems}
|
||||
additionalSubMenuItems={additionalSubMenuItems}
|
||||
timeSeries={timeSeries}
|
||||
/>
|
||||
</PanelHeader>
|
||||
<div className="panel-content">
|
||||
<DataPanel
|
||||
datasource={datasource}
|
||||
dataSourceApi={dataSourceApi}
|
||||
queries={targets}
|
||||
timeRange={timeRange}
|
||||
isVisible={this.isVisible}
|
||||
refreshCounter={refreshCounter}
|
||||
onIssueQueryResponse={this.onIssueQueryResponse}
|
||||
>
|
||||
{({ loading, timeSeries }) => {
|
||||
console.log('panelcrome inner render');
|
||||
|
@ -1,21 +1,16 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { PanelModel } from 'app/features/dashboard/panel_model';
|
||||
import { DashboardModel } from 'app/features/dashboard/dashboard_model';
|
||||
import { PanelHeaderMenu } from './PanelHeaderMenu';
|
||||
|
||||
interface PanelHeaderProps {
|
||||
panel: PanelModel;
|
||||
dashboard: DashboardModel;
|
||||
withMenuOptions: any;
|
||||
title: string;
|
||||
}
|
||||
|
||||
export class PanelHeader extends PureComponent<PanelHeaderProps, any> {
|
||||
render() {
|
||||
const { dashboard, withMenuOptions, panel } = this.props;
|
||||
const isFullscreen = false;
|
||||
const isLoading = false;
|
||||
const panelHeaderClass = classNames({ 'panel-header': true, 'grid-drag-handle': !isFullscreen });
|
||||
const PanelHeaderMenuComponent = withMenuOptions ? withMenuOptions(PanelHeaderMenu, panel) : PanelHeaderMenu;
|
||||
const { title } = this.props;
|
||||
|
||||
return (
|
||||
<div className={panelHeaderClass}>
|
||||
@ -34,10 +29,10 @@ export class PanelHeader extends PureComponent<PanelHeaderProps, any> {
|
||||
<div className="panel-title">
|
||||
<span className="icon-gf panel-alert-icon" />
|
||||
<span className="panel-title-text" data-toggle="dropdown">
|
||||
{this.props.panel.title} <span className="fa fa-caret-down panel-menu-toggle" />
|
||||
{title} <span className="fa fa-caret-down panel-menu-toggle" />
|
||||
</span>
|
||||
|
||||
<PanelHeaderMenuComponent panelId={panel.id} dashboard={dashboard} />
|
||||
{this.props.children}
|
||||
<span className="panel-time-info">
|
||||
<i className="fa fa-clock-o" /> 4m
|
||||
</span>
|
||||
|
@ -1,28 +1,25 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { DashboardModel } from 'app/features/dashboard/dashboard_model';
|
||||
import { PanelModel } from 'app/features/dashboard/panel_model';
|
||||
import { PanelHeaderMenuItem, PanelHeaderMenuItemProps } from './PanelHeaderMenuItem';
|
||||
import { getPanelMenu } from 'app/features/dashboard/utils/panel_menu';
|
||||
import { DataSourceApi } from 'app/types/series';
|
||||
import { TimeSeries } from 'app/types';
|
||||
|
||||
export interface PanelHeaderMenuProps {
|
||||
panelId: number;
|
||||
panel: PanelModel;
|
||||
dashboard: DashboardModel;
|
||||
datasource: any;
|
||||
dataSourceApi: DataSourceApi;
|
||||
additionalMenuItems?: PanelHeaderMenuItemProps[];
|
||||
additionalSubMenuItems?: PanelHeaderMenuItemProps[];
|
||||
timeSeries?: TimeSeries[];
|
||||
}
|
||||
|
||||
export class PanelHeaderMenu extends PureComponent<PanelHeaderMenuProps, any> {
|
||||
getPanel = () => {
|
||||
// Pass in panel as prop instead?
|
||||
const { panelId, dashboard } = this.props;
|
||||
const panelInfo = dashboard.getPanelInfoById(panelId);
|
||||
return panelInfo.panel;
|
||||
};
|
||||
|
||||
renderItems = (menu: PanelHeaderMenuItemProps[], isSubMenu = false) => {
|
||||
return (
|
||||
<ul className="dropdown-menu dropdown-menu--menu panel-menu" role={isSubMenu ? '' : 'menu'}>
|
||||
{menu.map((menuItem, idx) => {
|
||||
{menu.map((menuItem, idx: number) => {
|
||||
return (
|
||||
<PanelHeaderMenuItem
|
||||
key={idx} // TODO: Fix proper key
|
||||
@ -42,8 +39,8 @@ export class PanelHeaderMenu extends PureComponent<PanelHeaderMenuProps, any> {
|
||||
|
||||
render() {
|
||||
console.log('PanelHeaderMenu render');
|
||||
const { dashboard, additionalMenuItems, additionalSubMenuItems } = this.props;
|
||||
const menu = getPanelMenu(dashboard, this.getPanel(), additionalMenuItems, additionalSubMenuItems);
|
||||
const { dashboard, additionalMenuItems, additionalSubMenuItems, panel } = this.props;
|
||||
const menu = getPanelMenu(dashboard, panel, additionalMenuItems, additionalSubMenuItems);
|
||||
return <div className="panel-menu-container dropdown">{this.renderItems(menu)}</div>;
|
||||
}
|
||||
}
|
||||
|
@ -80,9 +80,11 @@ export const getPanelMenu = (
|
||||
handleClick: onEditPanelJson,
|
||||
});
|
||||
|
||||
additionalSubMenuItems.forEach(item => {
|
||||
menu.push(item);
|
||||
});
|
||||
if (additionalSubMenuItems) {
|
||||
additionalSubMenuItems.forEach(item => {
|
||||
menu.push(item);
|
||||
});
|
||||
}
|
||||
return menu;
|
||||
};
|
||||
|
||||
@ -115,9 +117,11 @@ export const getPanelMenu = (
|
||||
shortcut: 'p s',
|
||||
});
|
||||
|
||||
additionalMenuItems.forEach(item => {
|
||||
menu.push(item);
|
||||
});
|
||||
if (additionalMenuItems) {
|
||||
additionalMenuItems.forEach(item => {
|
||||
menu.push(item);
|
||||
});
|
||||
}
|
||||
|
||||
const subMenu: PanelHeaderMenuItemProps[] = getSubMenu();
|
||||
|
||||
|
@ -7,6 +7,8 @@ import { Switch } from 'app/core/components/Switch/Switch';
|
||||
import { getTimeSeriesVMs } from 'app/viz/state/timeSeries';
|
||||
import { PanelProps, PanelOptionsProps, NullValueMode } from 'app/types';
|
||||
|
||||
// import { moduleMenu } from './moduleMenu';
|
||||
|
||||
interface Options {
|
||||
showBars: boolean;
|
||||
showLines: boolean;
|
||||
@ -74,3 +76,4 @@ export class GraphOptions extends PureComponent<PanelOptionsProps<Options>> {
|
||||
|
||||
export { Graph2 as PanelComponent, GraphOptions as PanelOptionsComponent };
|
||||
export { withMenuOptions } from './withMenuOptions';
|
||||
export { moduleMenu } from './moduleMenu';
|
||||
|
76
public/app/plugins/panel/graph2/moduleMenu.tsx
Normal file
76
public/app/plugins/panel/graph2/moduleMenu.tsx
Normal file
@ -0,0 +1,76 @@
|
||||
import config from 'app/core/config';
|
||||
import { contextSrv } from 'app/core/services/context_srv';
|
||||
import { getExploreUrl } from 'app/core/utils/explore';
|
||||
import { updateLocation } from 'app/core/actions';
|
||||
import { getTimeSrv } from 'app/features/dashboard/time_srv';
|
||||
import { store } from 'app/store/configureStore';
|
||||
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
||||
import appEvents from 'app/core/app_events';
|
||||
|
||||
import {
|
||||
PanelHeaderMenuItemProps,
|
||||
PanelHeaderMenuItemTypes,
|
||||
} from 'app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenuItem';
|
||||
|
||||
export const moduleMenu = (panel, dataSourceApi, timeSeries) => {
|
||||
const onExploreClick = async () => {
|
||||
const datasourceSrv = getDatasourceSrv();
|
||||
const timeSrv = getTimeSrv();
|
||||
const url = await getExploreUrl(panel, panel.targets, dataSourceApi, datasourceSrv, timeSrv);
|
||||
if (url) {
|
||||
store.dispatch(updateLocation({ path: url }));
|
||||
}
|
||||
};
|
||||
|
||||
const onExportCsv = () => {
|
||||
const model = {} as { seriesList: string };
|
||||
model.seriesList = timeSeries;
|
||||
appEvents.emit('show-modal', {
|
||||
templateHtml: '<export-data-modal data="model.seriesList"></export-data-modal>',
|
||||
model,
|
||||
modalClass: 'modal--narrow',
|
||||
});
|
||||
};
|
||||
|
||||
const getAdditionalMenuItems = () => {
|
||||
const items = [];
|
||||
if (
|
||||
config.exploreEnabled &&
|
||||
contextSrv.isEditor &&
|
||||
dataSourceApi &&
|
||||
(dataSourceApi.meta.explore || dataSourceApi.meta.id === 'mixed')
|
||||
) {
|
||||
items.push({
|
||||
type: PanelHeaderMenuItemTypes.Link,
|
||||
text: 'Explore',
|
||||
handleClick: onExploreClick,
|
||||
iconClassName: 'fa fa-fw fa-rocket',
|
||||
shortcut: 'x',
|
||||
});
|
||||
}
|
||||
return items;
|
||||
};
|
||||
|
||||
const getAdditionalSubMenuItems = () => {
|
||||
return [
|
||||
{
|
||||
type: PanelHeaderMenuItemTypes.Link,
|
||||
text: 'Hello Sub Menu',
|
||||
handleClick: () => {
|
||||
alert('Hello world from moduleMenu');
|
||||
},
|
||||
shortcut: 'hi',
|
||||
},
|
||||
{
|
||||
type: PanelHeaderMenuItemTypes.Link,
|
||||
text: 'Export CSV',
|
||||
handleClick: onExportCsv,
|
||||
},
|
||||
] as PanelHeaderMenuItemProps[];
|
||||
};
|
||||
|
||||
return {
|
||||
getAdditionalMenuItems: getAdditionalMenuItems(),
|
||||
getAdditionalSubMenuItems: getAdditionalSubMenuItems(),
|
||||
};
|
||||
};
|
@ -14,6 +14,7 @@ export interface PluginExports {
|
||||
PanelComponent?: ComponentClass<PanelProps>;
|
||||
PanelOptionsComponent: ComponentClass<PanelOptionsProps>;
|
||||
withMenuOptions?: any;
|
||||
moduleMenu?: any;
|
||||
}
|
||||
|
||||
export interface PanelPlugin {
|
||||
|
Loading…
Reference in New Issue
Block a user