mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
NewPanelEdit: Adding repeating options (#22984)
* NewPanelEdit: Adding repeating options * Added all repeat options * reduce strict null errors
This commit is contained in:
parent
57c55f7e16
commit
441e87d4d4
@ -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>
|
||||
</>
|
||||
|
@ -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;
|
||||
}
|
@ -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>
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user