Commit 441e87d4 by Torkel Ödegaard Committed by GitHub

NewPanelEdit: Adding repeating options (#22984)

* NewPanelEdit: Adding repeating options

* Added all repeat options

* reduce strict null errors
parent 57c55f7e
......@@ -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>
</>
......
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;
}
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>
......
......@@ -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());
......
......@@ -29,7 +29,7 @@ export interface OwnProps {
}
export interface ConnectedProps {
plugin?: PanelPlugin;
plugin?: PanelPlugin | null;
}
export interface DispatchProps {
......
......@@ -31,7 +31,7 @@ interface OwnProps {
}
interface ConnectedProps {
angularComponent: AngularComponent;
angularComponent?: AngularComponent | null;
}
interface DispatchProps {
......
......@@ -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>
) : (
......
......@@ -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',
......
......@@ -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
......
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