From 6c0c1254fe65e098bec784cc74508845d61648fa Mon Sep 17 00:00:00 2001 From: Johannes Schill Date: Sat, 3 Nov 2018 23:36:40 +0100 Subject: [PATCH] wip: panel-header: More merge conflicts --- .../dashboard/dashgrid/DashboardPanel.tsx | 2 +- .../features/dashboard/dashgrid/DataPanel.tsx | 26 ++++--- .../dashboard/dashgrid/PanelChrome.tsx | 65 +++++++++++++--- .../dashgrid/PanelHeader/PanelHeader.tsx | 15 ++-- .../dashgrid/PanelHeader/PanelHeaderMenu.tsx | 21 +++-- .../features/dashboard/utils/panel_menu.ts | 16 ++-- public/app/plugins/panel/graph2/module.tsx | 3 + .../app/plugins/panel/graph2/moduleMenu.tsx | 76 +++++++++++++++++++ public/app/types/plugins.ts | 1 + 9 files changed, 174 insertions(+), 51 deletions(-) create mode 100644 public/app/plugins/panel/graph2/moduleMenu.tsx diff --git a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx index cf41595ce8c..d75e3abc67f 100644 --- a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx @@ -115,7 +115,6 @@ export class DashboardPanel extends PureComponent { 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 { withMenuOptions={pluginExports.withMenuOptions} panel={this.props.panel} dashboard={this.props.dashboard} + moduleMenu={pluginExports.moduleMenu} /> {this.props.panel.isEditing && ( diff --git a/public/app/features/dashboard/dashgrid/DataPanel.tsx b/public/app/features/dashboard/dashgrid/DataPanel.tsx index a42d392c018..77460d9dc83 100644 --- a/public/app/features/dashboard/dashgrid/DataPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DataPanel.tsx @@ -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 { } 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 { 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 { }; 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 { response: resp, isFirstLoad: false, }); + + this.props.onIssueQueryResponse(resp.data); } catch (err) { console.log('Loading error', err); this.setState({ loading: LoadingState.Error, isFirstLoad: false }); diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index 1a6f5a3cee2..4ac0723e4e8 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -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; - 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 { 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 ({ + ...prevState, refreshCounter: this.state.refreshCounter + 1, timeRange: timeRange, - }); + })); }; onRender = () => { @@ -63,27 +83,50 @@ export class PanelChrome extends PureComponent { + 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 (
- + + +
{({ loading, timeSeries }) => { console.log('panelcrome inner render'); diff --git a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeader.tsx b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeader.tsx index 3d23949afd0..ba5511014f2 100644 --- a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeader.tsx +++ b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeader.tsx @@ -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 { 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 (
@@ -34,10 +29,10 @@ export class PanelHeader extends PureComponent {
- {this.props.panel.title} + {title} - + {this.props.children} 4m diff --git a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenu.tsx b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenu.tsx index c36eb9d8584..dae9e33b996 100644 --- a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenu.tsx +++ b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenu.tsx @@ -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 { - 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 (
    - {menu.map((menuItem, idx) => { + {menu.map((menuItem, idx: number) => { return ( { 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
    {this.renderItems(menu)}
    ; } } diff --git a/public/app/features/dashboard/utils/panel_menu.ts b/public/app/features/dashboard/utils/panel_menu.ts index 67adf118edd..9d5fec4c6a4 100644 --- a/public/app/features/dashboard/utils/panel_menu.ts +++ b/public/app/features/dashboard/utils/panel_menu.ts @@ -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(); diff --git a/public/app/plugins/panel/graph2/module.tsx b/public/app/plugins/panel/graph2/module.tsx index 88b679e1645..c0a4fef8cfc 100644 --- a/public/app/plugins/panel/graph2/module.tsx +++ b/public/app/plugins/panel/graph2/module.tsx @@ -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> { export { Graph2 as PanelComponent, GraphOptions as PanelOptionsComponent }; export { withMenuOptions } from './withMenuOptions'; +export { moduleMenu } from './moduleMenu'; diff --git a/public/app/plugins/panel/graph2/moduleMenu.tsx b/public/app/plugins/panel/graph2/moduleMenu.tsx new file mode 100644 index 00000000000..64729e953a8 --- /dev/null +++ b/public/app/plugins/panel/graph2/moduleMenu.tsx @@ -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: '', + 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(), + }; +}; diff --git a/public/app/types/plugins.ts b/public/app/types/plugins.ts index 9ede3dd9f4b..e87eef38c04 100644 --- a/public/app/types/plugins.ts +++ b/public/app/types/plugins.ts @@ -14,6 +14,7 @@ export interface PluginExports { PanelComponent?: ComponentClass; PanelOptionsComponent: ComponentClass; withMenuOptions?: any; + moduleMenu?: any; } export interface PanelPlugin {