Commit 6c0c1254 by Johannes Schill

wip: panel-header: More merge conflicts

parent ca4612af
......@@ -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 && (
......
// 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');
......
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>
......
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';
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 {
......
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