mirror of
https://github.com/grafana/grafana.git
synced 2025-02-14 17:43:35 -06:00
PanelEditor: stores option group collapse state (#23781)
* PanelEditor: stores option group collapse state * Refactor: adds props to OptionsGroup instead
This commit is contained in:
parent
319a0585a5
commit
ca385805c9
@ -45,6 +45,7 @@ export const DynamicConfigValueEditor: React.FC<DynamicConfigValueEditorProps> =
|
|||||||
if (isCollapsible) {
|
if (isCollapsible) {
|
||||||
editor = (
|
editor = (
|
||||||
<OptionsGroup
|
<OptionsGroup
|
||||||
|
id={item.name}
|
||||||
renderTitle={renderLabel(false, true)}
|
renderTitle={renderLabel(false, true)}
|
||||||
className={css`
|
className={css`
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
|
@ -195,6 +195,7 @@ export const DefaultFieldConfigEditor: React.FC<Props> = ({ data, onChange, conf
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
id={`${k}/${i}`}
|
||||||
key={`${k}/${i}`}
|
key={`${k}/${i}`}
|
||||||
>
|
>
|
||||||
{groupedConfigs[k].map(c => {
|
{groupedConfigs[k].map(c => {
|
||||||
|
@ -1,18 +1,81 @@
|
|||||||
import React, { useState, FC, useEffect } from 'react';
|
import React, { FC, memo, useCallback, useEffect, useState } from 'react';
|
||||||
import { css, cx } from 'emotion';
|
import { css, cx } from 'emotion';
|
||||||
import { GrafanaTheme } from '@grafana/data';
|
import { GrafanaTheme } from '@grafana/data';
|
||||||
import { useTheme, Icon, stylesFactory } from '@grafana/ui';
|
import { Icon, stylesFactory, useTheme } from '@grafana/ui';
|
||||||
|
import { PANEL_EDITOR_UI_STATE_STORAGE_KEY } from './state/reducers';
|
||||||
|
import { useLocalStorage } from 'react-use';
|
||||||
|
|
||||||
interface Props {
|
export interface OptionsGroupProps {
|
||||||
|
id: string;
|
||||||
title?: React.ReactNode;
|
title?: React.ReactNode;
|
||||||
renderTitle?: (isExpanded: boolean) => React.ReactNode;
|
renderTitle?: (isExpanded: boolean) => React.ReactNode;
|
||||||
defaultToClosed?: boolean;
|
defaultToClosed?: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
nested?: boolean;
|
nested?: boolean;
|
||||||
|
persistMe?: boolean;
|
||||||
onToggle?: (isExpanded: boolean) => void;
|
onToggle?: (isExpanded: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const OptionsGroup: FC<Props> = ({
|
export const OptionsGroup: FC<OptionsGroupProps> = ({
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
children,
|
||||||
|
defaultToClosed,
|
||||||
|
renderTitle,
|
||||||
|
className,
|
||||||
|
nested = false,
|
||||||
|
persistMe = true,
|
||||||
|
onToggle,
|
||||||
|
}) => {
|
||||||
|
if (persistMe) {
|
||||||
|
return (
|
||||||
|
<CollapsibleSectionWithPersistence
|
||||||
|
id={id}
|
||||||
|
defaultToClosed={defaultToClosed}
|
||||||
|
className={className}
|
||||||
|
nested={nested}
|
||||||
|
renderTitle={renderTitle}
|
||||||
|
persistMe={persistMe}
|
||||||
|
title={title}
|
||||||
|
onToggle={onToggle}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</CollapsibleSectionWithPersistence>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CollapsibleSection
|
||||||
|
defaultToClosed={defaultToClosed}
|
||||||
|
className={className}
|
||||||
|
nested={nested}
|
||||||
|
renderTitle={renderTitle}
|
||||||
|
title={title}
|
||||||
|
onToggle={onToggle}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</CollapsibleSection>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const CollapsibleSectionWithPersistence: FC<OptionsGroupProps> = memo(props => {
|
||||||
|
const [value, setValue] = useLocalStorage(getOptionGroupStorageKey(props.id), {
|
||||||
|
defaultToClosed: props.defaultToClosed,
|
||||||
|
});
|
||||||
|
const onToggle = useCallback(
|
||||||
|
(isExpanded: boolean) => {
|
||||||
|
setValue({ defaultToClosed: !isExpanded });
|
||||||
|
if (props.onToggle) {
|
||||||
|
props.onToggle(isExpanded);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[setValue, props.onToggle]
|
||||||
|
);
|
||||||
|
|
||||||
|
return <CollapsibleSection {...props} defaultToClosed={value.defaultToClosed} onToggle={onToggle} />;
|
||||||
|
});
|
||||||
|
|
||||||
|
const CollapsibleSection: FC<Omit<OptionsGroupProps, 'id' | 'persistMe'>> = ({
|
||||||
title,
|
title,
|
||||||
children,
|
children,
|
||||||
defaultToClosed,
|
defaultToClosed,
|
||||||
@ -106,3 +169,5 @@ const getStyles = stylesFactory((theme: GrafanaTheme, isExpanded: boolean, isNes
|
|||||||
),
|
),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const getOptionGroupStorageKey = (id: string): string => `${PANEL_EDITOR_UI_STATE_STORAGE_KEY}.optionGroup[${id}]`;
|
||||||
|
@ -121,7 +121,7 @@ export const OverrideEditor: React.FC<OverrideEditorProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<OptionsGroup renderTitle={renderOverrideTitle}>
|
<OptionsGroup renderTitle={renderOverrideTitle} id={name} key={name}>
|
||||||
<Field label={matcherLabel} description={matcherUi.description}>
|
<Field label={matcherLabel} description={matcherUi.description}>
|
||||||
<matcherUi.component
|
<matcherUi.component
|
||||||
matcher={matcherUi.matcher}
|
matcher={matcherUi.matcher}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { PanelOptionsEditorItem, PanelPlugin } from '@grafana/data';
|
import { PanelOptionsEditorItem, PanelPlugin } from '@grafana/data';
|
||||||
import { set as lodashSet, get as lodashGet } from 'lodash';
|
import { get as lodashGet, set as lodashSet } from 'lodash';
|
||||||
import { Label, Field } from '@grafana/ui';
|
import { Field, Label } from '@grafana/ui';
|
||||||
import groupBy from 'lodash/groupBy';
|
import groupBy from 'lodash/groupBy';
|
||||||
import { OptionsGroup } from './OptionsGroup';
|
import { OptionsGroup } from './OptionsGroup';
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ export const PanelOptionsEditor: React.FC<PanelOptionsEditorProps<any>> = ({ plu
|
|||||||
.filter(e => e !== null);
|
.filter(e => e !== null);
|
||||||
|
|
||||||
return optionsToShow.length > 0 ? (
|
return optionsToShow.length > 0 ? (
|
||||||
<OptionsGroup title={c} defaultToClosed key={`${c}/${i}`}>
|
<OptionsGroup title={c} defaultToClosed id={`${c}/${i}`} key={`${c}/${i}`}>
|
||||||
<div>{optionsToShow}</div>
|
<div>{optionsToShow}</div>
|
||||||
</OptionsGroup>
|
</OptionsGroup>
|
||||||
) : null;
|
) : null;
|
||||||
|
@ -2,12 +2,12 @@ import React, { FC, useMemo, useRef } from 'react';
|
|||||||
import { DashboardModel, PanelModel } from '../../state';
|
import { DashboardModel, PanelModel } from '../../state';
|
||||||
import { FieldConfigSource, PanelData, PanelPlugin, SelectableValue } from '@grafana/data';
|
import { FieldConfigSource, PanelData, PanelPlugin, SelectableValue } from '@grafana/data';
|
||||||
import { Counter, DataLinksInlineEditor, Field, Input, RadioButtonGroup, Select, Switch, TextArea } from '@grafana/ui';
|
import { Counter, DataLinksInlineEditor, Field, Input, RadioButtonGroup, Select, Switch, TextArea } from '@grafana/ui';
|
||||||
import { OptionsGroup } from './OptionsGroup';
|
|
||||||
import { getPanelLinksVariableSuggestions } from '../../../panel/panellinks/link_srv';
|
import { getPanelLinksVariableSuggestions } from '../../../panel/panellinks/link_srv';
|
||||||
import { getVariables } from '../../../variables/state/selectors';
|
import { getVariables } from '../../../variables/state/selectors';
|
||||||
import { PanelOptionsEditor } from './PanelOptionsEditor';
|
import { PanelOptionsEditor } from './PanelOptionsEditor';
|
||||||
import { AngularPanelOptions } from './AngularPanelOptions';
|
import { AngularPanelOptions } from './AngularPanelOptions';
|
||||||
import { VisualizationTab } from './VisualizationTab';
|
import { VisualizationTab } from './VisualizationTab';
|
||||||
|
import { OptionsGroup } from './OptionsGroup';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
panel: PanelModel;
|
panel: PanelModel;
|
||||||
@ -48,7 +48,7 @@ export const PanelOptionsTab: FC<Props> = ({
|
|||||||
};
|
};
|
||||||
// Fist common panel settings Title, description
|
// Fist common panel settings Title, description
|
||||||
elements.push(
|
elements.push(
|
||||||
<OptionsGroup title="Panel settings" key="Panel settings">
|
<OptionsGroup title="Panel settings" id="Panel settings" key="Panel settings">
|
||||||
<Field label="Panel title">
|
<Field label="Panel title">
|
||||||
<Input defaultValue={panel.title} onBlur={e => onPanelConfigChange('title', e.currentTarget.value)} />
|
<Input defaultValue={panel.title} onBlur={e => onPanelConfigChange('title', e.currentTarget.value)} />
|
||||||
</Field>
|
</Field>
|
||||||
@ -65,7 +65,7 @@ export const PanelOptionsTab: FC<Props> = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
elements.push(
|
elements.push(
|
||||||
<OptionsGroup title="Panel type" key="Panel type" defaultToClosed onToggle={focusVisPickerInput}>
|
<OptionsGroup title="Panel type" id="Panel type" key="Panel type" defaultToClosed onToggle={focusVisPickerInput}>
|
||||||
<VisualizationTab panel={panel} ref={visTabInputRef} />
|
<VisualizationTab panel={panel} ref={visTabInputRef} />
|
||||||
</OptionsGroup>
|
</OptionsGroup>
|
||||||
);
|
);
|
||||||
@ -73,7 +73,7 @@ export const PanelOptionsTab: FC<Props> = ({
|
|||||||
// Old legacy react editor
|
// Old legacy react editor
|
||||||
if (plugin.editor && panel && !plugin.optionEditors) {
|
if (plugin.editor && panel && !plugin.optionEditors) {
|
||||||
elements.push(
|
elements.push(
|
||||||
<OptionsGroup title="Display" key="legacy react editor">
|
<OptionsGroup title="Display" id="legacy react editor" key="legacy react editor">
|
||||||
<plugin.editor
|
<plugin.editor
|
||||||
data={data}
|
data={data}
|
||||||
options={panel.getOptions()}
|
options={panel.getOptions()}
|
||||||
@ -107,8 +107,9 @@ export const PanelOptionsTab: FC<Props> = ({
|
|||||||
renderTitle={isExpanded => (
|
renderTitle={isExpanded => (
|
||||||
<>Panel links {!isExpanded && panelLinksCount > 0 && <Counter value={panelLinksCount} />}</>
|
<>Panel links {!isExpanded && panelLinksCount > 0 && <Counter value={panelLinksCount} />}</>
|
||||||
)}
|
)}
|
||||||
|
id="panel links"
|
||||||
key="panel links"
|
key="panel links"
|
||||||
defaultToClosed={true}
|
defaultToClosed
|
||||||
>
|
>
|
||||||
<DataLinksInlineEditor
|
<DataLinksInlineEditor
|
||||||
links={panel.links}
|
links={panel.links}
|
||||||
@ -120,7 +121,7 @@ export const PanelOptionsTab: FC<Props> = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
elements.push(
|
elements.push(
|
||||||
<OptionsGroup title="Panel repeats" key="panel repeats" defaultToClosed={true}>
|
<OptionsGroup title="Panel repeats" id="panel repeats" key="panel repeats" defaultToClosed>
|
||||||
<Field
|
<Field
|
||||||
label="Repeat by variable"
|
label="Repeat by variable"
|
||||||
description="Repeat this panel for each value in the selected variable.
|
description="Repeat this panel for each value in the selected variable.
|
||||||
|
Loading…
Reference in New Issue
Block a user