NewPanelEdit: Adding repeating options (#22984)

* NewPanelEdit: Adding repeating options

* Added all repeat options

* reduce strict null errors
This commit is contained in:
Torkel Ödegaard 2020-03-24 15:44:13 +01:00 committed by GitHub
parent 57c55f7e16
commit 441e87d4d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 127 additions and 68 deletions

View File

@ -101,7 +101,7 @@ export const DataLinksInlineEditor: React.FC<DataLinksInlineEditorProps> = ({ li
<FullWidthButtonContainer>
<Forms.Button size="sm" icon="fa fa-plus" onClick={onDataLinkAdd}>
Add data link
Add link
</Forms.Button>
</FullWidthButtonContainer>
</>

View File

@ -0,0 +1,105 @@
import React, { useMemo, FC } from 'react';
import { PanelModel } from '../../state';
import { SelectableValue } from '@grafana/data';
import { Forms, DataLinksInlineEditor } from '@grafana/ui';
import { OptionsGroup } from './OptionsGroup';
import { getPanelLinksVariableSuggestions } from '../../../panel/panellinks/link_srv';
import { getVariables } from '../../../variables/state/selectors';
export const GeneralPanelOptions: FC<{
panel: PanelModel;
onPanelConfigChange: (configKey: string, value: any) => void;
}> = ({ panel, onPanelConfigChange }) => {
const linkVariablesSuggestions = useMemo(() => getPanelLinksVariableSuggestions(), []);
const variableOptions = getVariableOptions();
const directionOptions = [
{ label: 'Horizontal', value: 'h' },
{ label: 'Vertical', value: 'v' },
];
const maxPerRowOptions = [2, 3, 4, 6, 8, 12].map(value => ({ label: value.toString(), value }));
return (
<div>
<OptionsGroup title="Panel settings">
<Forms.Field label="Panel title">
<Forms.Input defaultValue={panel.title} onBlur={e => onPanelConfigChange('title', e.currentTarget.value)} />
</Forms.Field>
<Forms.Field label="Description" description="Panel description supports markdown and links.">
<Forms.TextArea
defaultValue={panel.description}
onBlur={e => onPanelConfigChange('description', e.currentTarget.value)}
/>
</Forms.Field>
<Forms.Field label="Transparent" description="Display panel without background.">
<Forms.Switch
value={panel.transparent}
onChange={e => onPanelConfigChange('transparent', e.currentTarget.checked)}
/>
</Forms.Field>
</OptionsGroup>
<OptionsGroup title="Panel links">
<DataLinksInlineEditor
links={panel.links}
onChange={links => onPanelConfigChange('links', links)}
suggestions={linkVariablesSuggestions}
data={[]}
/>
</OptionsGroup>
<OptionsGroup title="Panel repeats">
<Forms.Field
label="Repeat by variable"
description="Repeat this panel for each value in the selected variable.
This is not visible while in edit mode. You need to go back to dashboard and then update the variable or
reload the dashboard."
>
<Forms.Select
value={panel.repeat}
onChange={value => onPanelConfigChange('repeat', value.value)}
options={variableOptions}
/>
</Forms.Field>
{panel.repeat && (
<Forms.Field label="Repeat direction">
<Forms.RadioButtonGroup
options={directionOptions}
value={panel.repeatDirection || 'h'}
onChange={value => onPanelConfigChange('repeatDirection', value)}
/>
</Forms.Field>
)}
{panel.repeat && panel.repeatDirection === 'h' && (
<Forms.Field label="Max per row">
<Forms.Select
options={maxPerRowOptions}
value={panel.maxPerRow}
onChange={value => onPanelConfigChange('maxPerRow', value.value)}
/>
</Forms.Field>
)}
</OptionsGroup>
</div>
);
};
function getVariableOptions(): Array<SelectableValue<string>> {
const options = getVariables().map((item: any) => {
return { label: item.name, value: item.name };
});
if (options.length === 0) {
options.unshift({
label: 'No template variables found',
value: null,
});
}
options.unshift({
label: 'Disable repeating',
value: null,
});
return options;
}

View File

@ -1,22 +1,11 @@
import React, { useCallback, useState, useMemo } from 'react';
import React, { useCallback, useState } from 'react';
import { FieldConfigSource, GrafanaTheme, PanelData, PanelPlugin } from '@grafana/data';
import { DashboardModel, PanelModel } from '../../state';
import {
CustomScrollbar,
stylesFactory,
Tab,
TabContent,
TabsBar,
useTheme,
Forms,
DataLinksInlineEditor,
Container,
} from '@grafana/ui';
import { CustomScrollbar, stylesFactory, Tab, TabContent, TabsBar, useTheme, Container } from '@grafana/ui';
import { DefaultFieldConfigEditor, OverrideFieldConfigEditor } from './FieldConfigEditor';
import { AngularPanelOptions } from './AngularPanelOptions';
import { css } from 'emotion';
import { OptionsGroup } from './OptionsGroup';
import { getPanelLinksVariableSuggestions } from '../../../panel/panellinks/link_srv';
import { GeneralPanelOptions } from './GeneralPanelOptions';
export const OptionsPaneContent: React.FC<{
plugin?: PanelPlugin;
@ -30,7 +19,6 @@ export const OptionsPaneContent: React.FC<{
const theme = useTheme();
const styles = getStyles(theme);
const linkVariablesSuggestions = useMemo(() => getPanelLinksVariableSuggestions(), []);
const renderFieldOptions = useCallback(
(plugin: PanelPlugin) => {
const fieldConfig = panel.getFieldConfig();
@ -53,6 +41,7 @@ export const OptionsPaneContent: React.FC<{
},
[data, plugin, panel, onFieldConfigsChange]
);
const renderFieldOverrideOptions = useCallback(
(plugin: PanelPlugin) => {
const fieldConfig = panel.getFieldConfig();
@ -98,47 +87,6 @@ export const OptionsPaneContent: React.FC<{
[data, plugin, panel, onFieldConfigsChange]
);
const renderPanelSettings = useCallback(() => {
console.log(panel.transparent);
return (
<div>
<OptionsGroup title="Panel settings">
<>
<Forms.Field label="Panel title">
<Forms.Input
defaultValue={panel.title}
onBlur={e => onPanelConfigChange('title', e.currentTarget.value)}
/>
</Forms.Field>
<Forms.Field label="Description" description="Panel description supports markdown and links">
<Forms.TextArea
defaultValue={panel.description}
onBlur={e => onPanelConfigChange('description', e.currentTarget.value)}
/>
</Forms.Field>
<Forms.Field label="Transparent" description="Display panel without background">
<Forms.Switch
value={panel.transparent}
onChange={e => onPanelConfigChange('transparent', e.currentTarget.checked)}
/>
</Forms.Field>
</>
</OptionsGroup>
<OptionsGroup title="Panel links">
<DataLinksInlineEditor
links={panel.links}
onChange={links => onPanelConfigChange('links', links)}
suggestions={linkVariablesSuggestions}
data={data.series}
/>
</OptionsGroup>
<OptionsGroup title="Panel repeating">
<div>TODO</div>
</OptionsGroup>
</div>
);
}, [data, plugin, panel, onFieldConfigsChange]);
const [activeTab, setActiveTab] = useState('defaults');
return (
@ -154,7 +102,7 @@ export const OptionsPaneContent: React.FC<{
<CustomScrollbar>
{activeTab === 'defaults' && renderFieldOptions(plugin)}
{activeTab === 'overrides' && renderFieldOverrideOptions(plugin)}
{activeTab === 'panel' && renderPanelSettings()}
{activeTab === 'panel' && <GeneralPanelOptions panel={panel} onPanelConfigChange={onPanelConfigChange} />}
</CustomScrollbar>
</TabContent>
</div>

View File

@ -35,6 +35,7 @@ export function panelEditorCleanUp(): ThunkResult<void> {
return (dispatch, getStore) => {
const dashboard = getStore().dashboard.getModel();
const { getPanel, getSourcePanel, querySubscription, shouldDiscardChanges } = getStore().panelEditorNew;
if (!shouldDiscardChanges) {
const panel = getPanel();
const modifiedSaveModel = panel.getSaveModel();
@ -47,7 +48,7 @@ export function panelEditorCleanUp(): ThunkResult<void> {
sourcePanel.restoreModel(modifiedSaveModel);
if (panelTypeChanged) {
dispatch(panelModelAndPluginReady({ panelId: sourcePanel.id, plugin: panel.plugin }));
dispatch(panelModelAndPluginReady({ panelId: sourcePanel.id, plugin: panel.plugin! }));
}
// Resend last query result on source panel query runner
@ -57,8 +58,13 @@ export function panelEditorCleanUp(): ThunkResult<void> {
}, 20);
}
dashboard.exitPanelEditor();
querySubscription.unsubscribe();
if (dashboard) {
dashboard.exitPanelEditor();
}
if (querySubscription) {
querySubscription.unsubscribe();
}
dispatch(cleanUpEditPanel());
dispatch(closeCompleted());

View File

@ -29,7 +29,7 @@ export interface OwnProps {
}
export interface ConnectedProps {
plugin?: PanelPlugin;
plugin?: PanelPlugin | null;
}
export interface DispatchProps {

View File

@ -31,7 +31,7 @@ interface OwnProps {
}
interface ConnectedProps {
angularComponent: AngularComponent;
angularComponent?: AngularComponent | null;
}
interface DispatchProps {

View File

@ -22,7 +22,7 @@ export interface Props {
title?: string;
description?: string;
scopedVars?: ScopedVars;
angularComponent?: AngularComponent;
angularComponent?: AngularComponent | null;
links?: DataLink[];
error?: string;
isFullscreen: boolean;
@ -108,7 +108,7 @@ export class PanelHeader extends Component<Props, State> {
return (
<Tooltip content={notice.text} key={notice.severity}>
{notice.inspect ? (
<div className="panel-info-notice" onClick={e => this.openInspect(e, notice.inspect)}>
<div className="panel-info-notice" onClick={e => this.openInspect(e, notice.inspect!)}>
<span className="fa fa-info-circle" style={{ marginRight: '8px', cursor: 'pointer' }} />
</div>
) : (

View File

@ -15,7 +15,7 @@ import { PanelCtrl } from '../../panel/panel_ctrl';
export function getPanelMenu(
dashboard: DashboardModel,
panel: PanelModel,
angularComponent?: AngularComponent
angularComponent?: AngularComponent | null
): PanelMenuItem[] {
const onViewPanel = (event: React.MouseEvent<any>) => {
event.preventDefault();
@ -127,7 +127,7 @@ export function getPanelMenu(
shortcut: 'p s',
});
if (contextSrv.hasAccessToExplore() && !panel.plugin.meta.skipDataQuery) {
if (contextSrv.hasAccessToExplore() && !(panel.plugin && panel.plugin.meta.skipDataQuery)) {
menu.push({
text: 'Explore',
iconClassName: 'gicon gicon-explore',

View File

@ -3,7 +3,7 @@
echo -e "Collecting code stats (typescript errors & more)"
ERROR_COUNT_LIMIT=816
ERROR_COUNT_LIMIT=812
DIRECTIVES_LIMIT=172
CONTROLLERS_LIMIT=139