Panels: No title will no longer make panel header take up space (#16884)

* Panels: Initial poc of no panel titles

* moving panel-container to DashboardPanel

* Starting to work

* Gauge fix

* Panels: tweaked panel padding and title z-index stuff

* Panels: changed panel padding a bit and simplified it by having same padding in vertical and horizontal

* Lots of tweaks to panel padding related stuff

* Updated snapshot

* Added test dashboard

* Final refactorings

* Trim title befgor applying panel-container--no-title class

* Remove unnecessary type annotation

* Panels: hasTitle no need to trim

* Gauge: fixed font family
This commit is contained in:
Torkel Ödegaard
2019-05-06 15:26:09 +02:00
committed by GitHub
parent 32a45077e7
commit 48ae93048b
23 changed files with 1177 additions and 236 deletions

View File

@@ -0,0 +1,904 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": "-- Grafana --",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"gnetId": null,
"graphTooltip": 0,
"links": [],
"panels": [
{
"cacheTimeout": null,
"colorBackground": false,
"colorValue": false,
"colors": ["#299c46", "#5794F2", "#d44a3a"],
"description": "asdasdas",
"format": "ms",
"gauge": {
"maxValue": 100,
"minValue": 0,
"show": false,
"thresholdLabels": false,
"thresholdMarkers": true
},
"gridPos": {
"h": 4,
"w": 11,
"x": 0,
"y": 0
},
"id": 8,
"interval": null,
"links": [],
"mappingType": 1,
"mappingTypes": [
{
"name": "value to text",
"value": 1
},
{
"name": "range to text",
"value": 2
}
],
"maxDataPoints": 100,
"nullPointMode": "connected",
"nullText": null,
"postfix": "",
"postfixFontSize": "50%",
"prefix": "",
"prefixFontSize": "100%",
"rangeMaps": [
{
"from": "null",
"text": "N/A",
"to": "null"
}
],
"sparkline": {
"fillColor": "rgba(31, 118, 189, 0.18)",
"full": true,
"lineColor": "rgb(31, 120, 193)",
"show": true
},
"tableColumn": "",
"targets": [
{
"refId": "A",
"scenarioId": "random_walk"
}
],
"thresholds": "",
"timeFrom": null,
"timeShift": "2h",
"title": "Title",
"type": "singlestat",
"valueFontSize": "120%",
"valueMaps": [
{
"op": "=",
"text": "N/A",
"value": "null"
}
],
"valueName": "avg"
},
{
"cacheTimeout": null,
"colorBackground": false,
"colorValue": false,
"colors": ["#299c46", "#5794F2", "#d44a3a"],
"description": "asdasdas",
"format": "ms",
"gauge": {
"maxValue": 100,
"minValue": 0,
"show": false,
"thresholdLabels": false,
"thresholdMarkers": true
},
"gridPos": {
"h": 4,
"w": 13,
"x": 11,
"y": 0
},
"id": 2,
"interval": null,
"links": [],
"mappingType": 1,
"mappingTypes": [
{
"name": "value to text",
"value": 1
},
{
"name": "range to text",
"value": 2
}
],
"maxDataPoints": 100,
"nullPointMode": "connected",
"nullText": null,
"postfix": "",
"postfixFontSize": "50%",
"prefix": "",
"prefixFontSize": "100%",
"rangeMaps": [
{
"from": "null",
"text": "N/A",
"to": "null"
}
],
"sparkline": {
"fillColor": "rgba(31, 118, 189, 0.18)",
"full": true,
"lineColor": "rgb(31, 120, 193)",
"show": true
},
"tableColumn": "",
"targets": [
{
"refId": "A",
"scenarioId": "random_walk"
}
],
"thresholds": "",
"timeFrom": null,
"timeShift": "2h",
"title": "",
"type": "singlestat",
"valueFontSize": "120%",
"valueMaps": [
{
"op": "=",
"text": "N/A",
"value": "null"
}
],
"valueName": "avg"
},
{
"cacheTimeout": null,
"colorBackground": false,
"colorValue": false,
"colors": ["#299c46", "#5794F2", "#d44a3a"],
"description": "asdasdas",
"format": "ms",
"gauge": {
"maxValue": 100,
"minValue": 0,
"show": false,
"thresholdLabels": false,
"thresholdMarkers": true
},
"gridPos": {
"h": 4,
"w": 11,
"x": 0,
"y": 4
},
"id": 4,
"interval": null,
"links": [],
"mappingType": 1,
"mappingTypes": [
{
"name": "value to text",
"value": 1
},
{
"name": "range to text",
"value": 2
}
],
"maxDataPoints": 100,
"nullPointMode": "connected",
"nullText": null,
"postfix": "",
"postfixFontSize": "50%",
"prefix": "",
"prefixFontSize": "100%",
"rangeMaps": [
{
"from": "null",
"text": "N/A",
"to": "null"
}
],
"sparkline": {
"fillColor": "rgba(31, 118, 189, 0.18)",
"full": true,
"lineColor": "rgb(31, 120, 193)",
"show": true
},
"tableColumn": "",
"targets": [
{
"refId": "A",
"scenarioId": "random_walk"
}
],
"thresholds": "",
"timeFrom": null,
"timeShift": "2h",
"title": "Panel Title",
"type": "singlestat",
"valueFontSize": "120%",
"valueMaps": [
{
"op": "=",
"text": "N/A",
"value": "null"
}
],
"valueName": "avg"
},
{
"cacheTimeout": null,
"description": "asdasdas",
"gridPos": {
"h": 4,
"w": 6,
"x": 11,
"y": 4
},
"id": 9,
"links": [],
"options": {
"maxValue": 100,
"minValue": 0,
"orientation": "auto",
"showThresholdLabels": false,
"showThresholdMarkers": true,
"thresholds": [
{
"color": "green",
"index": 0,
"value": null
},
{
"color": "red",
"index": 1,
"value": 80
}
],
"valueMappings": [],
"valueOptions": {
"decimals": null,
"prefix": "",
"stat": "mean",
"suffix": "",
"unit": "none"
}
},
"pluginVersion": "6.2.0-pre",
"targets": [
{
"refId": "A",
"scenarioId": "random_walk"
}
],
"timeFrom": null,
"timeShift": "2h",
"title": "Panel Title",
"type": "gauge"
},
{
"cacheTimeout": null,
"description": "asdasdas",
"gridPos": {
"h": 4,
"w": 7,
"x": 17,
"y": 4
},
"id": 11,
"links": [],
"options": {
"maxValue": 100,
"minValue": 0,
"orientation": "auto",
"showThresholdLabels": false,
"showThresholdMarkers": true,
"thresholds": [
{
"color": "green",
"index": 0,
"value": null
},
{
"color": "red",
"index": 1,
"value": 80
}
],
"valueMappings": [],
"valueOptions": {
"decimals": null,
"prefix": "",
"stat": "mean",
"suffix": "",
"unit": "none"
}
},
"pluginVersion": "6.2.0-pre",
"targets": [
{
"refId": "A",
"scenarioId": "random_walk"
}
],
"timeFrom": null,
"timeShift": "2h",
"title": "Panel Title",
"type": "gauge"
},
{
"gridPos": {
"h": 4,
"w": 11,
"x": 0,
"y": 8
},
"id": 15,
"links": [],
"options": {
"content": "# Title\n\nFor markdown syntax help: [commonmark.org/help](https://commonmark.org/help/)\n\n\n",
"mode": "markdown"
},
"timeFrom": null,
"timeShift": null,
"title": "",
"type": "text2"
},
{
"cacheTimeout": null,
"colorBackground": false,
"colorValue": false,
"colors": ["#299c46", "rgba(237, 129, 40, 0.89)", "#d44a3a"],
"description": "asdasdas",
"format": "none",
"gauge": {
"maxValue": 100,
"minValue": 0,
"show": true,
"thresholdLabels": false,
"thresholdMarkers": true
},
"gridPos": {
"h": 4,
"w": 6,
"x": 11,
"y": 8
},
"id": 10,
"interval": null,
"links": [],
"mappingType": 1,
"mappingTypes": [
{
"name": "value to text",
"value": 1
},
{
"name": "range to text",
"value": 2
}
],
"maxDataPoints": 100,
"nullPointMode": "connected",
"nullText": null,
"pluginVersion": "6.2.0-pre",
"postfix": "",
"postfixFontSize": "50%",
"prefix": "",
"prefixFontSize": "50%",
"rangeMaps": [
{
"from": "null",
"text": "N/A",
"to": "null"
}
],
"sparkline": {
"fillColor": "rgba(31, 118, 189, 0.18)",
"full": false,
"lineColor": "rgb(31, 120, 193)",
"show": false
},
"tableColumn": "",
"targets": [
{
"refId": "A",
"scenarioId": "random_walk"
}
],
"thresholds": "",
"timeFrom": null,
"timeShift": "2h",
"title": "",
"type": "singlestat",
"valueFontSize": "80%",
"valueMaps": [
{
"op": "=",
"text": "N/A",
"value": "null"
}
],
"valueName": "avg"
},
{
"cacheTimeout": null,
"description": "asdasdas",
"gridPos": {
"h": 4,
"w": 7,
"x": 17,
"y": 8
},
"id": 12,
"links": [],
"options": {
"maxValue": 100,
"minValue": 0,
"orientation": "auto",
"showThresholdLabels": false,
"showThresholdMarkers": true,
"thresholds": [
{
"color": "green",
"index": 0,
"value": null
},
{
"color": "red",
"index": 1,
"value": 80
}
],
"valueMappings": [],
"valueOptions": {
"decimals": null,
"prefix": "",
"stat": "mean",
"suffix": "",
"unit": "none"
}
},
"pluginVersion": "6.2.0-pre",
"targets": [
{
"refId": "A",
"scenarioId": "random_walk"
}
],
"timeFrom": null,
"timeShift": "2h",
"title": "",
"type": "gauge"
},
{
"cacheTimeout": null,
"description": "",
"gridPos": {
"h": 6,
"w": 11,
"x": 0,
"y": 12
},
"id": 3,
"links": [],
"options": {
"displayMode": "gradient",
"fieldOptions": {
"defaults": {
"max": 100,
"min": 0
},
"mappings": [],
"override": {},
"stats": ["mean"],
"thresholds": [
{
"color": "green",
"index": 0,
"value": null
},
{
"color": "red",
"index": 1,
"value": 80
}
],
"title": "Testing",
"values": false
},
"maxValue": 100,
"minValue": 0,
"orientation": "horizontal",
"thresholds": [
{
"color": "green",
"index": 0,
"value": null
},
{
"color": "red",
"index": 1,
"value": 80
}
],
"valueMappings": [],
"valueOptions": {
"decimals": null,
"prefix": "",
"stat": "mean",
"suffix": "",
"unit": "none"
}
},
"targets": [
{
"refId": "A",
"scenarioId": "random_walk"
},
{
"refId": "B",
"scenarioId": "random_walk"
}
],
"timeFrom": null,
"timeShift": "2h",
"title": "",
"type": "bargauge"
},
{
"cacheTimeout": null,
"description": "asdasdas",
"gridPos": {
"h": 6,
"w": 13,
"x": 11,
"y": 12
},
"id": 5,
"links": [],
"options": {
"displayMode": "gradient",
"fieldOptions": {
"defaults": {
"max": 100,
"min": 0
},
"mappings": [],
"override": {},
"stats": ["mean"],
"thresholds": [
{
"color": "green",
"index": 0,
"value": null
},
{
"color": "red",
"index": 1,
"value": 80
}
],
"title": "Testing",
"values": false
},
"maxValue": 100,
"minValue": 0,
"orientation": "horizontal",
"thresholds": [
{
"color": "green",
"index": 0,
"value": null
},
{
"color": "red",
"index": 1,
"value": 80
}
],
"valueMappings": [],
"valueOptions": {
"decimals": null,
"prefix": "",
"stat": "mean",
"suffix": "",
"unit": "none"
}
},
"targets": [
{
"refId": "A",
"scenarioId": "slow_query",
"stringInput": "1s"
},
{
"refId": "B",
"scenarioId": "slow_query",
"stringInput": "1s"
}
],
"timeFrom": null,
"timeShift": "2h",
"title": "My cool pane title",
"type": "bargauge"
},
{
"cacheTimeout": null,
"description": "asdasdas",
"gridPos": {
"h": 4,
"w": 24,
"x": 0,
"y": 18
},
"id": 7,
"links": [],
"options": {
"displayMode": "gradient",
"fieldOptions": {
"defaults": {
"max": 100,
"min": 0
},
"mappings": [],
"override": {},
"stats": ["mean"],
"thresholds": [
{
"color": "green",
"index": 0,
"value": null
},
{
"color": "red",
"index": 1,
"value": 80
}
],
"title": "Testing",
"values": false
},
"maxValue": 100,
"minValue": 0,
"orientation": "horizontal",
"thresholds": [
{
"color": "green",
"index": 0,
"value": null
},
{
"color": "red",
"index": 1,
"value": 80
}
],
"valueMappings": [],
"valueOptions": {
"decimals": null,
"prefix": "",
"stat": "mean",
"suffix": "",
"unit": "none"
}
},
"targets": [
{
"refId": "A",
"scenarioId": "slow_query",
"stringInput": "10s"
}
],
"timeFrom": null,
"timeShift": null,
"title": "My cool pane title",
"type": "bargauge"
},
{
"aliasColors": {},
"bars": false,
"cacheTimeout": null,
"dashLength": 10,
"dashes": false,
"description": "asdasdas",
"fill": 1,
"gridPos": {
"h": 6,
"w": 11,
"x": 0,
"y": 22
},
"id": 6,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"links": [],
"nullPointMode": "null",
"percentage": false,
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"refId": "A",
"scenarioId": "slow_query",
"stringInput": "5s"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "My cool pane title",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {},
"bars": false,
"cacheTimeout": null,
"dashLength": 10,
"dashes": false,
"description": "",
"fill": 1,
"gridPos": {
"h": 6,
"w": 13,
"x": 11,
"y": 22
},
"id": 13,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"links": [],
"nullPointMode": "null",
"percentage": false,
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"refId": "A",
"scenarioId": "slow_query",
"stringInput": "5s"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
}
],
"schemaVersion": 18,
"style": "dark",
"tags": ["gdev", "panel-tests"],
"templating": {
"list": []
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {
"refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"],
"time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"]
},
"timezone": "",
"title": "Panel Tests - With & Without title",
"uid": "2WaUpZmWk",
"version": 23
}

View File

@@ -182,10 +182,8 @@ exports[`Render should render with base threshold 1`] = `
"sm": "24px", "sm": "24px",
}, },
"name": "Grafana Dark", "name": "Grafana Dark",
"panelPadding": Object { "panelHeaderHeight": 28,
"horizontal": 16, "panelPadding": 8,
"vertical": 8,
},
"spacing": Object { "spacing": Object {
"d": "14px", "d": "14px",
"gutter": "30px", "gutter": "30px",
@@ -345,10 +343,8 @@ exports[`Render should render with base threshold 1`] = `
"sm": "24px", "sm": "24px",
}, },
"name": "Grafana Dark", "name": "Grafana Dark",
"panelPadding": Object { "panelHeaderHeight": 28,
"horizontal": 16, "panelPadding": 8,
"vertical": 8,
},
"spacing": Object { "spacing": Object {
"d": "14px", "d": "14px",
"gutter": "30px", "gutter": "30px",

View File

@@ -74,12 +74,12 @@ export class VizRepeater<T> extends PureComponent<Props<T>, State<T>> {
if (orientation === VizOrientation.Horizontal) { if (orientation === VizOrientation.Horizontal) {
repeaterStyle.flexDirection = 'column'; repeaterStyle.flexDirection = 'column';
itemStyles.margin = `${itemSpacing / 2}px 0`; itemStyles.marginBottom = `${itemSpacing}px`;
vizWidth = width; vizWidth = width;
vizHeight = height / values.length - itemSpacing; vizHeight = height / values.length - itemSpacing;
} else { } else {
repeaterStyle.flexDirection = 'row'; repeaterStyle.flexDirection = 'row';
itemStyles.margin = `0 ${itemSpacing / 2}px`; itemStyles.marginRight = `${itemSpacing}px`;
vizHeight = height; vizHeight = height;
vizWidth = width / values.length - itemSpacing; vizWidth = width / values.length - itemSpacing;
} }

View File

@@ -197,9 +197,9 @@ $side-menu-width: 60px;
// dashboard // dashboard
$dashboard-padding: $space-md; $dashboard-padding: $space-md;
$panel-padding: 0 ${theme.panelPadding.horizontal}px ${theme.panelPadding.vertical}px ${ $panel-padding: ${theme.panelPadding}px;
theme.panelPadding.horizontal $panel-header-height: ${theme.panelHeaderHeight}px;
}px; $panel-header-z-index: 10;
// tabs // tabs
$tabs-padding: 10px 15px 9px; $tabs-padding: 10px 15px 9px;

View File

@@ -72,10 +72,8 @@ const theme: GrafanaThemeCommons = {
md: '32px', md: '32px',
lg: '48px', lg: '48px',
}, },
panelPadding: { panelPadding: 8,
horizontal: 16, panelHeaderHeight: 28,
vertical: 8,
},
zIndex: { zIndex: {
dropdown: '1000', dropdown: '1000',
navbarFixed: '1020', navbarFixed: '1020',

View File

@@ -77,10 +77,8 @@ export interface GrafanaThemeCommons {
md: string; md: string;
lg: string; lg: string;
}; };
panelPadding: { panelPadding: number;
horizontal: number; panelHeaderHeight: number;
vertical: number;
};
zIndex: { zIndex: {
dropdown: string; dropdown: string;
navbarFixed: string; navbarFixed: string;

View File

@@ -9,8 +9,4 @@ export const MIN_PANEL_HEIGHT = GRID_CELL_HEIGHT * 3;
export const LS_PANEL_COPY_KEY = 'panel-copy'; export const LS_PANEL_COPY_KEY = 'panel-copy';
export const DASHBOARD_TOOLBAR_HEIGHT = 55;
export const DASHBOARD_TOP_PADDING = 20;
export const PANEL_HEADER_HEIGHT = 27;
export const PANEL_BORDER = 2; export const PANEL_BORDER = 2;

View File

@@ -129,16 +129,21 @@ export class DashboardPanel extends PureComponent<Props, State> {
this.props.dashboard.setPanelFocus(0); this.props.dashboard.setPanelFocus(0);
}; };
renderReactPanel() { renderPanel() {
const { dashboard, panel, isFullscreen, isInView } = this.props; const { dashboard, panel, isFullscreen, isInView } = this.props;
const { plugin } = this.state; const { plugin } = this.state;
if (plugin.angularPanelCtrl) {
return <div ref={element => (this.element = element)} className="panel-height-helper" />;
}
return ( return (
<AutoSizer> <AutoSizer>
{({ width, height }) => { {({ width, height }) => {
if (width === 0) { if (width === 0) {
return null; return null;
} }
return ( return (
<PanelChrome <PanelChrome
plugin={plugin} plugin={plugin}
@@ -155,10 +160,6 @@ export class DashboardPanel extends PureComponent<Props, State> {
); );
} }
renderAngularPanel() {
return <div ref={element => (this.element = element)} className="panel-height-helper" />;
}
render() { render() {
const { panel, dashboard, isFullscreen, isEditing } = this.props; const { panel, dashboard, isFullscreen, isEditing } = this.props;
const { plugin, angularPanel, isLazy } = this.state; const { plugin, angularPanel, isLazy } = this.state;
@@ -177,7 +178,11 @@ export class DashboardPanel extends PureComponent<Props, State> {
return null; return null;
} }
const containerClass = classNames({ 'panel-editor-container': isEditing, 'panel-height-helper': !isEditing }); const editorContainerClasses = classNames({
'panel-editor-container': isEditing,
'panel-height-helper': !isEditing,
});
const panelWrapperClass = classNames({ const panelWrapperClass = classNames({
'panel-wrapper': true, 'panel-wrapper': true,
'panel-wrapper--edit': isEditing, 'panel-wrapper--edit': isEditing,
@@ -185,7 +190,7 @@ export class DashboardPanel extends PureComponent<Props, State> {
}); });
return ( return (
<div className={containerClass}> <div className={editorContainerClasses}>
<PanelResizer <PanelResizer
isEditing={isEditing} isEditing={isEditing}
panel={panel} panel={panel}
@@ -196,7 +201,7 @@ export class DashboardPanel extends PureComponent<Props, State> {
onMouseLeave={this.onMouseLeave} onMouseLeave={this.onMouseLeave}
style={styles} style={styles}
> >
{plugin.angularPanelCtrl ? this.renderAngularPanel() : this.renderReactPanel()} {this.renderPanel()}
</div> </div>
)} )}
/> />

View File

@@ -1,17 +1,18 @@
// Libraries // Libraries
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import classNames from 'classnames';
// Services import { Unsubscribable } from 'rxjs';
import { getTimeSrv, TimeSrv } from '../services/TimeSrv';
// Components // Components
import { PanelHeader } from './PanelHeader/PanelHeader'; import { PanelHeader } from './PanelHeader/PanelHeader';
import ErrorBoundary from 'app/core/components/ErrorBoundary/ErrorBoundary'; import ErrorBoundary from 'app/core/components/ErrorBoundary/ErrorBoundary';
// Utils // Utils & Services
import { applyPanelTimeOverrides } from 'app/features/dashboard/utils/panel'; import { getTimeSrv, TimeSrv } from '../services/TimeSrv';
import { PANEL_HEADER_HEIGHT } from 'app/core/constants'; import { applyPanelTimeOverrides, calculateInnerPanelHeight } from 'app/features/dashboard/utils/panel';
import { profiler } from 'app/core/profiler'; import { profiler } from 'app/core/profiler';
import { getProcessedSeriesData } from '../state/PanelQueryState';
import templateSrv from 'app/features/templating/template_srv';
import config from 'app/core/config'; import config from 'app/core/config';
// Types // Types
@@ -19,11 +20,6 @@ import { DashboardModel, PanelModel } from '../state';
import { LoadingState, PanelData, PanelPlugin } from '@grafana/ui'; import { LoadingState, PanelData, PanelPlugin } from '@grafana/ui';
import { ScopedVars } from '@grafana/ui'; import { ScopedVars } from '@grafana/ui';
import templateSrv from 'app/features/templating/template_srv';
import { getProcessedSeriesData } from '../state/PanelQueryState';
import { Unsubscribable } from 'rxjs';
const DEFAULT_PLUGIN_ERROR = 'Error in plugin'; const DEFAULT_PLUGIN_ERROR = 'Error in plugin';
export interface Props { export interface Props {
@@ -232,7 +228,7 @@ export class PanelChrome extends PureComponent<Props, State> {
renderPanel(width: number, height: number): JSX.Element { renderPanel(width: number, height: number): JSX.Element {
const { panel, plugin } = this.props; const { panel, plugin } = this.props;
const { renderCounter, data, isFirstLoad } = this.state; const { renderCounter, data, isFirstLoad } = this.state;
const PanelComponent = plugin.panel; const { theme } = config;
// This is only done to increase a counter that is used by backend // This is only done to increase a counter that is used by backend
// image rendering (phantomjs/headless chrome) to know when to capture image // image rendering (phantomjs/headless chrome) to know when to capture image
@@ -246,6 +242,9 @@ export class PanelChrome extends PureComponent<Props, State> {
return this.renderLoadingState(); return this.renderLoadingState();
} }
const PanelComponent = plugin.panel;
const innerPanelHeight = calculateInnerPanelHeight(panel, height);
return ( return (
<> <>
{loading === LoadingState.Loading && this.renderLoadingState()} {loading === LoadingState.Loading && this.renderLoadingState()}
@@ -254,8 +253,8 @@ export class PanelChrome extends PureComponent<Props, State> {
data={data} data={data}
timeRange={data.request ? data.request.range : this.timeSrv.timeRange()} timeRange={data.request ? data.request.range : this.timeSrv.timeRange()}
options={panel.getOptions(plugin.defaults)} options={panel.getOptions(plugin.defaults)}
width={width - 2 * config.theme.panelPadding.horizontal} width={width - theme.panelPadding * 2}
height={height - PANEL_HEADER_HEIGHT - config.theme.panelPadding.vertical} height={innerPanelHeight}
renderCounter={renderCounter} renderCounter={renderCounter}
replaceVariables={this.replaceVariables} replaceVariables={this.replaceVariables}
onOptionsChange={this.onOptionsChange} onOptionsChange={this.onOptionsChange}
@@ -278,7 +277,13 @@ export class PanelChrome extends PureComponent<Props, State> {
const { errorMessage, data } = this.state; const { errorMessage, data } = this.state;
const { transparent } = panel; const { transparent } = panel;
const containerClassNames = `panel-container panel-container--absolute ${transparent ? 'panel-transparent' : ''}`; const containerClassNames = classNames({
'panel-container': true,
'panel-container--absolute': true,
'panel-container--no-title': !panel.hasTitle(),
'panel-transparent': transparent,
});
return ( return (
<div className={containerClassNames}> <div className={containerClassNames}>
<PanelHeader <PanelHeader

View File

@@ -72,10 +72,13 @@ export class PanelHeader extends Component<Props, State> {
render() { render() {
const { panel, dashboard, timeInfo, scopedVars, error, isFullscreen } = this.props; const { panel, dashboard, timeInfo, scopedVars, error, isFullscreen } = this.props;
const panelHeaderClass = classNames({ 'panel-header': true, 'grid-drag-handle': !isFullscreen });
const title = templateSrv.replaceWithText(panel.title, scopedVars); const title = templateSrv.replaceWithText(panel.title, scopedVars);
const panelHeaderClass = classNames({
'panel-header': true,
'grid-drag-handle': !isFullscreen,
});
return ( return (
<> <>
<PanelHeaderCorner <PanelHeaderCorner

View File

@@ -326,6 +326,10 @@ export class PanelModel {
return this.queryRunner; return this.queryRunner;
} }
hasTitle() {
return !!this.title.length;
}
destroy() { destroy() {
this.events.emit('panel-teardown'); this.events.emit('panel-teardown');
this.events.removeAllListeners(); this.events.removeAllListeners();

View File

@@ -11,12 +11,13 @@ import { isString as _isString } from 'lodash';
import * as rangeUtil from '@grafana/ui/src/utils/rangeutil'; import * as rangeUtil from '@grafana/ui/src/utils/rangeutil';
import * as dateMath from '@grafana/ui/src/utils/datemath'; import * as dateMath from '@grafana/ui/src/utils/datemath';
import appEvents from 'app/core/app_events'; import appEvents from 'app/core/app_events';
import config from 'app/core/config';
// Services // Services
import templateSrv from 'app/features/templating/template_srv'; import templateSrv from 'app/features/templating/template_srv';
// Constants // Constants
import { LS_PANEL_COPY_KEY } from 'app/core/constants'; import { LS_PANEL_COPY_KEY, PANEL_BORDER } from 'app/core/constants';
export const removePanel = (dashboard: DashboardModel, panel: PanelModel, ask: boolean) => { export const removePanel = (dashboard: DashboardModel, panel: PanelModel, ask: boolean) => {
// confirm deletion // confirm deletion
@@ -169,3 +170,12 @@ export function getResolution(panel: PanelModel): number {
return panel.maxDataPoints ? panel.maxDataPoints : Math.ceil(width * (panel.gridPos.w / 24)); return panel.maxDataPoints ? panel.maxDataPoints : Math.ceil(width * (panel.gridPos.w / 24));
} }
export function calculateInnerPanelHeight(panel: PanelModel, containerHeight: number): number {
return (
containerHeight -
(panel.hasTitle() ? config.theme.panelHeaderHeight : 0) -
config.theme.panelPadding * 2 -
PANEL_BORDER
);
}

View File

@@ -11,9 +11,10 @@ import {
copyPanel as copyPanelUtil, copyPanel as copyPanelUtil,
editPanelJson as editPanelJsonUtil, editPanelJson as editPanelJsonUtil,
sharePanel as sharePanelUtil, sharePanel as sharePanelUtil,
calculateInnerPanelHeight,
} from 'app/features/dashboard/utils/panel'; } from 'app/features/dashboard/utils/panel';
import { GRID_COLUMN_COUNT, PANEL_HEADER_HEIGHT, PANEL_BORDER } from 'app/core/constants'; import { GRID_COLUMN_COUNT } from 'app/core/constants';
export class PanelCtrl { export class PanelCtrl {
panel: any; panel: any;
@@ -202,7 +203,7 @@ export class PanelCtrl {
calculatePanelHeight(containerHeight) { calculatePanelHeight(containerHeight) {
this.containerHeight = containerHeight; this.containerHeight = containerHeight;
this.height = this.containerHeight - (PANEL_BORDER + PANEL_HEADER_HEIGHT); this.height = calculateInnerPanelHeight(this.panel, containerHeight);
} }
render(payload?) { render(payload?) {

View File

@@ -6,7 +6,7 @@ import baron from 'baron';
const module = angular.module('grafana.directives'); const module = angular.module('grafana.directives');
const panelTemplate = ` const panelTemplate = `
<div class="panel-container"> <div class="panel-container" ng-class="{'panel-container--no-title': !ctrl.panel.title.length}">
<div class="panel-header" ng-class="{'grid-drag-handle': !ctrl.panel.fullscreen}"> <div class="panel-header" ng-class="{'grid-drag-handle': !ctrl.panel.fullscreen}">
<span class="panel-info-corner"> <span class="panel-info-corner">
<i class="fa"></i> <i class="fa"></i>

View File

@@ -430,7 +430,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
const plotCanvas = $('<div></div>'); const plotCanvas = $('<div></div>');
const plotCss = { const plotCss = {
top: '10px', top: '5px',
margin: 'auto', margin: 'auto',
position: 'relative', position: 'relative',
height: height * 0.9 + 'px', height: height * 0.9 + 'px',
@@ -494,7 +494,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
}, },
font: { font: {
size: fontSize, size: fontSize,
family: '"Helvetica Neue", Helvetica, Arial, sans-serif', family: config.theme.typography.fontFamily.sansSerif,
}, },
}, },
show: true, show: true,
@@ -512,7 +512,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
} }
function addSparkline() { function addSparkline() {
const width = elem.width() + 20; const width = elem.width();
if (width < 30) { if (width < 30) {
// element has not gotten it's width yet // element has not gotten it's width yet
// delay sparkline render // delay sparkline render
@@ -524,17 +524,16 @@ class SingleStatCtrl extends MetricsPanelCtrl {
const plotCanvas = $('<div></div>'); const plotCanvas = $('<div></div>');
const plotCss: any = {}; const plotCss: any = {};
plotCss.position = 'absolute'; plotCss.position = 'absolute';
plotCss.bottom = '0px';
if (panel.sparkline.full) { if (panel.sparkline.full) {
plotCss.bottom = '5px'; plotCss.left = '0px';
plotCss.left = '-5px'; plotCss.width = width + 'px';
plotCss.width = width - 10 + 'px';
const dynamicHeightMargin = height <= 100 ? 5 : Math.round(height / 100) * 15 + 5; const dynamicHeightMargin = height <= 100 ? 5 : Math.round(height / 100) * 15 + 5;
plotCss.height = height - dynamicHeightMargin + 'px'; plotCss.height = height - dynamicHeightMargin + 'px';
} else { } else {
plotCss.bottom = '0px'; plotCss.left = '0px';
plotCss.left = '-5px'; plotCss.width = width + 'px';
plotCss.width = width - 10 + 'px';
plotCss.height = Math.floor(height * 0.25) + 'px'; plotCss.height = Math.floor(height * 0.25) + 'px';
} }

View File

@@ -99,6 +99,7 @@
@import 'components/page_loader'; @import 'components/page_loader';
@import 'components/toggle_button_group'; @import 'components/toggle_button_group';
@import 'components/popover-box'; @import 'components/popover-box';
@import 'components/panel_header';
// LOAD @grafana/ui components // LOAD @grafana/ui components
@import '../../packages/grafana-ui/src/index'; @import '../../packages/grafana-ui/src/index';

View File

@@ -200,7 +200,9 @@ $side-menu-width: 60px;
// dashboard // dashboard
$dashboard-padding: $space-md; $dashboard-padding: $space-md;
$panel-padding: 0 16px 8px 16px; $panel-padding: 8px;
$panel-header-height: 28px;
$panel-header-z-index: 10;
// tabs // tabs
$tabs-padding: 10px 15px 9px; $tabs-padding: 10px 15px 9px;

View File

@@ -6,6 +6,7 @@
.panel-wrapper { .panel-wrapper {
height: 100%; height: 100%;
position: relative;
&--edit { &--edit {
height: 40%; height: 40%;

View File

@@ -352,7 +352,7 @@
.left-yaxis-label { .left-yaxis-label {
top: 50%; top: 50%;
left: 2px; left: 8px;
transform: translateX(-50%) translateY(-50%) rotate(-90deg); transform: translateX(-50%) translateY(-50%) rotate(-90deg);
// this is needed for phantomsjs 2.1 // this is needed for phantomsjs 2.1
-webkit-transform: translateX(-50%) translateY(-50%) rotate(-90deg); -webkit-transform: translateX(-50%) translateY(-50%) rotate(-90deg);
@@ -360,7 +360,7 @@
.right-yaxis-label { .right-yaxis-label {
top: 50%; top: 50%;
right: 2px; right: 8px;
transform: translateX(50%) translateY(-50%) rotate(90deg); transform: translateX(50%) translateY(-50%) rotate(90deg);
// this is needed for phantomsjs 2.1 // this is needed for phantomsjs 2.1
-webkit-transform: translateX(50%) translateY(-50%) rotate(90deg); -webkit-transform: translateX(50%) translateY(-50%) rotate(90deg);

View File

@@ -0,0 +1,176 @@
$panel-header-no-title-zindex: 1;
.panel-header {
&:hover {
transition: background-color 0.1s ease-in-out;
background-color: $panel-header-hover-bg;
}
}
.panel-container--no-title {
.panel-header {
position: absolute;
width: 100%;
z-index: $panel-header-z-index;
}
.panel-content {
height: 100%;
}
}
.panel-title-container {
cursor: move;
word-wrap: break-word;
display: block;
}
.panel-title {
border: 0px;
font-weight: $font-weight-semi-bold;
position: relative;
width: 100%;
display: flex;
flex-wrap: nowrap;
justify-content: center;
height: $panel-header-height;
align-items: center;
}
.panel-title-text {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
max-width: calc(100% - 38px);
cursor: pointer;
font-weight: $font-weight-semi-bold;
&:hover {
color: $link-hover-color;
}
.panel-has-alert & {
max-width: calc(100% - 54px);
}
}
.panel-menu-container {
width: 1px;
height: 19px;
display: inline-block;
}
.panel-menu-toggle {
color: $text-color-weak;
cursor: pointer;
padding: 3px 5px;
visibility: hidden;
opacity: 0;
width: 16px;
height: 16px;
left: 1px;
top: 2px;
&:hover {
color: $link-hover-color;
}
}
.panel-loading {
position: absolute;
top: 4px;
right: 4px;
z-index: $panel-header-z-index + 1;
font-size: $font-size-sm;
color: $text-color-weak;
}
.panel-empty {
display: flex;
align-items: center;
height: 100%;
width: 100%;
p {
text-align: center;
color: $text-muted;
font-size: $font-size-lg;
width: 100%;
}
}
.panel-menu {
top: 25px;
left: -100px;
}
.panel-info-corner-inner {
width: 0;
height: 0;
position: absolute;
left: 0;
bottom: 0;
}
@mixin panel-corner-color($corner-bg) {
.panel-info-corner-inner {
border-left: $panel-header-height solid $corner-bg;
border-right: none;
border-bottom: $panel-header-height solid transparent;
}
}
.panel-info-corner {
color: $text-muted;
cursor: pointer;
position: absolute;
display: none;
left: 0;
width: $panel-header-height;
height: $panel-header-height;
top: 0;
.fa {
position: relative;
top: -2px;
left: 6px;
font-size: 75%;
z-index: $panel-header-no-title-zindex + 2;
}
&--info {
display: block;
@include panel-corner-color(lighten($panel-corner, 4%));
.fa:before {
content: '\f129';
}
}
&--links {
display: block;
@include panel-corner-color(lighten($panel-corner, 4%));
.fa {
left: 4px;
}
.fa:before {
content: '\f08e';
}
}
&--error {
display: block;
color: $white;
@include panel-corner-color($popover-error-bg);
.fa:before {
content: '\f12a';
}
}
}
.panel-time-info {
font-weight: $font-weight-semi-bold;
float: right;
margin-right: 8px;
color: $blue;
font-size: 85%;
position: absolute;
right: 0;
}

View File

@@ -14,6 +14,15 @@
z-index: 1; z-index: 1;
font-size: 3em; font-size: 3em;
font-weight: $font-weight-semi-bold; font-weight: $font-weight-semi-bold;
// helps make the title feel more centered when there is a panel title
padding-bottom: $panel-padding;
}
// Helps
.panel-container--no-title {
.singlestat-panel-value-container {
padding-bottom: 0;
}
} }
.singlestat-panel-prefix { .singlestat-panel-prefix {
@@ -21,5 +30,5 @@
} }
#flotGagueValue0 { #flotGagueValue0 {
font-weight: bold; //please dont hurt me for this! font-weight: $font-weight-semi-bold; //please dont hurt me for this!
} }

View File

@@ -79,9 +79,9 @@ div.flot-text {
.panel-content { .panel-content {
padding: $panel-padding; padding: $panel-padding;
height: calc(100% - 27px); height: calc(100% - #{$panel-header-height});
width: 100%;
position: relative; position: relative;
// Fixes scrolling on mobile devices // Fixes scrolling on mobile devices
overflow: auto; overflow: auto;
} }
@@ -93,172 +93,6 @@ div.flot-text {
} }
} }
.panel-title-container {
min-height: 9px;
cursor: move;
word-wrap: break-word;
display: block;
}
.panel-title {
border: 0px;
font-weight: $font-weight-semi-bold;
position: relative;
width: 100%;
display: flex;
flex-wrap: nowrap;
justify-content: center;
padding: 4px 0 4px;
}
.panel-title-text {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
max-width: calc(100% - 38px);
cursor: pointer;
font-weight: $font-weight-semi-bold;
&:hover {
color: $link-hover-color;
}
.panel-has-alert & {
max-width: calc(100% - 54px);
}
}
.panel-menu-container {
width: 1px;
height: 19px;
display: inline-block;
}
.panel-menu-toggle {
color: $text-color-weak;
cursor: pointer;
padding: 3px 5px;
visibility: hidden;
opacity: 0;
width: 16px;
height: 16px;
left: 1px;
top: 2px;
&:hover {
color: $link-hover-color;
}
}
.panel-loading {
position: absolute;
top: -3px;
right: 0px;
z-index: 800;
font-size: $font-size-sm;
color: $text-color-weak;
}
.panel-empty {
display: flex;
align-items: center;
height: 100%;
width: 100%;
p {
text-align: center;
color: $text-muted;
font-size: $font-size-lg;
width: 100%;
}
}
.panel-header {
&:hover {
transition: background-color 0.1s ease-in-out;
background-color: $panel-header-hover-bg;
}
}
.panel-menu {
top: 25px;
left: -100px;
}
.panel-info-corner-inner {
width: 0;
height: 0;
position: absolute;
left: 0;
bottom: 0;
}
@mixin panel-corner-color($corner-bg) {
.panel-info-corner-inner {
border-left: 27px solid $corner-bg;
border-right: none;
border-bottom: 27px solid transparent;
}
}
.panel-info-corner {
color: $text-muted;
cursor: pointer;
position: absolute;
display: none;
left: 0;
width: 27px;
height: 27px;
top: 0;
z-index: 1;
.fa {
position: relative;
top: -2px;
left: 6px;
font-size: 75%;
z-index: 1;
}
&--info {
display: block;
@include panel-corner-color(lighten($panel-corner, 4%));
.fa:before {
content: '\f129';
}
}
&--links {
display: block;
@include panel-corner-color(lighten($panel-corner, 4%));
.fa {
left: 4px;
}
.fa:before {
content: '\f08e';
}
}
&--error {
display: block;
color: $white;
@include panel-corner-color($popover-error-bg);
.fa:before {
content: '\f12a';
}
}
}
.panel-time-info {
font-weight: bold;
float: right;
margin-right: 15px;
color: $blue;
font-size: 85%;
position: absolute;
top: 4px;
right: 0;
}
.dashboard-header { .dashboard-header {
font-size: $font-size-h3; font-size: $font-size-h3;
text-align: center; text-align: center;

View File

@@ -164,7 +164,6 @@
padding: $space-sm $space-md 0 $space-md; padding: $space-sm $space-md 0 $space-md;
display: flex; display: flex;
cursor: pointer; cursor: pointer;
margin-bottom: $space-sm;
transition: all 0.1s linear; transition: all 0.1s linear;
} }