PanelEditor: Fixed issue changing a panel from transparent back to normal (#24483)

* PanelModel: Fixed issues with persisting some changes

* Fixed other issues
This commit is contained in:
Torkel Ödegaard
2020-05-11 22:03:43 +02:00
committed by GitHub
parent 3f31028bf8
commit 5caf7f59e6
5 changed files with 50 additions and 35 deletions

View File

@@ -106,7 +106,6 @@ export const OptionsPaneContent: React.FC<Props> = ({
dashboard={dashboard}
data={currentData}
onPanelConfigChange={onPanelConfigChange}
onFieldConfigsChange={onFieldConfigsChange}
onPanelOptionsChanged={onPanelOptionsChanged}
/>
) : (

View File

@@ -1,6 +1,6 @@
import React, { FC, useMemo, useRef } from 'react';
import { DashboardModel, PanelModel } from '../../state';
import { FieldConfigSource, PanelData, PanelPlugin, SelectableValue } from '@grafana/data';
import { PanelData, PanelPlugin, SelectableValue } from '@grafana/data';
import { Counter, DataLinksInlineEditor, Field, Input, RadioButtonGroup, Select, Switch, TextArea } from '@grafana/ui';
import { getPanelLinksVariableSuggestions } from '../../../panel/panellinks/link_srv';
import { getVariables } from '../../../variables/state/selectors';
@@ -16,7 +16,6 @@ interface Props {
dashboard: DashboardModel;
onPanelConfigChange: (configKey: string, value: any) => void;
onPanelOptionsChanged: (options: any) => void;
onFieldConfigsChange: (config: FieldConfigSource) => void;
}
export const PanelOptionsTab: FC<Props> = ({
@@ -26,7 +25,6 @@ export const PanelOptionsTab: FC<Props> = ({
dashboard,
onPanelConfigChange,
onPanelOptionsChanged,
onFieldConfigsChange,
}) => {
const visTabInputRef = useRef<HTMLInputElement>();
const linkVariablesSuggestions = useMemo(() => getPanelLinksVariableSuggestions(), []);

View File

@@ -1,5 +1,5 @@
// Libraries
import React, { PureComponent, ChangeEvent, FocusEvent, ReactText } from 'react';
import React, { PureComponent, ChangeEvent, FocusEvent } from 'react';
// Utils
import { rangeUtil, PanelData, DataSourceApi } from '@grafana/data';
@@ -49,7 +49,7 @@ interface State {
relativeTime: string;
timeShift: string;
cacheTimeout: string;
maxDataPoints: string | ReactText;
maxDataPoints: number | string;
interval: string;
hideTimeOverride: boolean;
isOpen: boolean;
@@ -63,7 +63,7 @@ export class QueryOptions extends PureComponent<Props, State> {
relativeTime: props.panel.timeFrom || '',
timeShift: props.panel.timeShift || '',
cacheTimeout: props.panel.cacheTimeout || '',
maxDataPoints: props.panel.maxDataPoints || '',
maxDataPoints: props.panel.maxDataPoints ?? '',
interval: props.panel.interval || '',
hideTimeOverride: props.panel.hideTimeOverride || false,
isOpen: false,
@@ -124,6 +124,20 @@ export class QueryOptions extends PureComponent<Props, State> {
this.setState({ ...this.state, [panelKey]: event.target.value });
};
onMaxDataPointsBlur = () => {
const { panel } = this.props;
const maxDataPoints = parseInt(this.state.maxDataPoints as string, 10);
if (isNaN(maxDataPoints)) {
delete panel.maxDataPoints;
} else {
panel.maxDataPoints = maxDataPoints;
}
panel.refresh();
};
renderCacheTimeoutOption() {
const { dataSource } = this.props;
const { cacheTimeout } = this.state;
@@ -176,12 +190,12 @@ export class QueryOptions extends PureComponent<Props, State> {
Max data points
</InlineFormLabel>
<Input
type="text"
type="number"
className="width-6"
placeholder={`${realMd}`}
name={name}
spellCheck={false}
onBlur={this.onDataSourceOptionBlur('maxDataPoints')}
onBlur={this.onMaxDataPointsBlur}
onChange={this.onDataSourceOptionChange('maxDataPoints')}
value={maxDataPoints}
/>

View File

@@ -86,6 +86,8 @@ describe('PanelModel', () => {
modelJson = {
type: 'table',
maxDataPoints: 100,
interval: '5m',
showColumns: true,
targets: [{ refId: 'A' }, { noRefId: true }],
options: persistedOptionsMock,
@@ -225,6 +227,14 @@ describe('PanelModel', () => {
expect(model.editSourceId).toBe(1001);
});
it('should keep maxDataPoints', () => {
expect(model.maxDataPoints).toBe(100);
});
it('should keep interval', () => {
expect(model.interval).toBe('5m');
});
it('should apply next panel option defaults', () => {
expect(model.getOptions().showThresholdLabels).toBeFalsy();
expect(model.getOptions().showThresholds).toBeUndefined();
@@ -313,18 +323,6 @@ describe('PanelModel', () => {
expect(model.someProperty).toBeUndefined();
});
it('Should preserve must keep properties', () => {
model.id = 10;
model.gridPos = { x: 0, y: 0, h: 10, w: 10 };
model.restoreModel({
title: 'New title',
options: {},
});
expect(model.id).toBe(10);
expect(model.gridPos.h).toBe(10);
});
it('Should remove old angular panel specfic props', () => {
model.axes = [{ prop: 1 }];
model.thresholds = [];
@@ -337,6 +335,13 @@ describe('PanelModel', () => {
expect(model.axes).toBeUndefined();
expect(model.thresholds).toBeUndefined();
});
it('Should be able to set defaults back to default', () => {
model.transparent = true;
model.restoreModel({});
expect(model.transparent).toBe(false);
});
});
describe('destroy', () => {

View File

@@ -82,6 +82,8 @@ const mustKeepProps: { [str: string]: boolean } = {
transformations: true,
fieldConfig: true,
editSourceId: true,
maxDataPoints: true,
interval: true,
};
const defaults: any = {
@@ -90,6 +92,7 @@ const defaults: any = {
cachedPluginOptions: {},
transparent: false,
options: {},
datasource: null,
};
export class PanelModel implements DataConfigSource {
@@ -146,9 +149,6 @@ export class PanelModel implements DataConfigSource {
constructor(model: any) {
this.events = new Emitter();
// should not be part of defaults as defaults are removed in save model and
// this should not be removed in save model as exporter needs to templatize it
this.datasource = null;
this.restoreModel(model);
this.replaceVariables = this.replaceVariables.bind(this);
}
@@ -156,12 +156,8 @@ export class PanelModel implements DataConfigSource {
/** Given a persistened PanelModel restores property values */
restoreModel(model: any) {
// Start with clean-up
for (const property of Object.keys(this)) {
if (notPersistedProperties[property]) {
continue;
}
if (mustKeepProps[property]) {
for (const property in this) {
if (notPersistedProperties[property] || !this.hasOwnProperty(property)) {
continue;
}
@@ -169,10 +165,6 @@ export class PanelModel implements DataConfigSource {
continue;
}
if (!this.hasOwnProperty(property)) {
continue;
}
if (typeof (this as any)[property] === 'function') {
continue;
}
@@ -215,7 +207,6 @@ export class PanelModel implements DataConfigSource {
updateOptions(options: object) {
this.options = options;
this.render();
}
@@ -228,6 +219,7 @@ export class PanelModel implements DataConfigSource {
getSaveModel() {
const model: any = {};
for (const property in this) {
if (notPersistedProperties[property] || !this.hasOwnProperty(property)) {
continue;
@@ -239,6 +231,13 @@ export class PanelModel implements DataConfigSource {
model[property] = _.cloneDeep(this[property]);
}
if (model.datasource === undefined) {
// This is part of defaults as defaults are removed in save model and
// this should not be removed in save model as exporter needs to templatize it
model.datasource = null;
}
return model;
}