mirror of
https://github.com/grafana/grafana.git
synced 2025-02-14 01:23:32 -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) {
|
||||
editor = (
|
||||
<OptionsGroup
|
||||
id={item.name}
|
||||
renderTitle={renderLabel(false, true)}
|
||||
className={css`
|
||||
padding-left: 0;
|
||||
|
@ -195,6 +195,7 @@ export const DefaultFieldConfigEditor: React.FC<Props> = ({ data, onChange, conf
|
||||
</>
|
||||
);
|
||||
}}
|
||||
id={`${k}/${i}`}
|
||||
key={`${k}/${i}`}
|
||||
>
|
||||
{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 { 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;
|
||||
renderTitle?: (isExpanded: boolean) => React.ReactNode;
|
||||
defaultToClosed?: boolean;
|
||||
className?: string;
|
||||
nested?: boolean;
|
||||
persistMe?: boolean;
|
||||
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,
|
||||
children,
|
||||
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 (
|
||||
<OptionsGroup renderTitle={renderOverrideTitle}>
|
||||
<OptionsGroup renderTitle={renderOverrideTitle} id={name} key={name}>
|
||||
<Field label={matcherLabel} description={matcherUi.description}>
|
||||
<matcherUi.component
|
||||
matcher={matcherUi.matcher}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { PanelOptionsEditorItem, PanelPlugin } from '@grafana/data';
|
||||
import { set as lodashSet, get as lodashGet } from 'lodash';
|
||||
import { Label, Field } from '@grafana/ui';
|
||||
import { get as lodashGet, set as lodashSet } from 'lodash';
|
||||
import { Field, Label } from '@grafana/ui';
|
||||
import groupBy from 'lodash/groupBy';
|
||||
import { OptionsGroup } from './OptionsGroup';
|
||||
|
||||
@ -50,7 +50,7 @@ export const PanelOptionsEditor: React.FC<PanelOptionsEditorProps<any>> = ({ plu
|
||||
.filter(e => e !== null);
|
||||
|
||||
return optionsToShow.length > 0 ? (
|
||||
<OptionsGroup title={c} defaultToClosed key={`${c}/${i}`}>
|
||||
<OptionsGroup title={c} defaultToClosed id={`${c}/${i}`} key={`${c}/${i}`}>
|
||||
<div>{optionsToShow}</div>
|
||||
</OptionsGroup>
|
||||
) : null;
|
||||
|
@ -2,12 +2,12 @@ import React, { FC, useMemo, useRef } from 'react';
|
||||
import { DashboardModel, PanelModel } from '../../state';
|
||||
import { FieldConfigSource, PanelData, PanelPlugin, SelectableValue } from '@grafana/data';
|
||||
import { Counter, DataLinksInlineEditor, Field, Input, RadioButtonGroup, Select, Switch, TextArea } from '@grafana/ui';
|
||||
import { OptionsGroup } from './OptionsGroup';
|
||||
import { getPanelLinksVariableSuggestions } from '../../../panel/panellinks/link_srv';
|
||||
import { getVariables } from '../../../variables/state/selectors';
|
||||
import { PanelOptionsEditor } from './PanelOptionsEditor';
|
||||
import { AngularPanelOptions } from './AngularPanelOptions';
|
||||
import { VisualizationTab } from './VisualizationTab';
|
||||
import { OptionsGroup } from './OptionsGroup';
|
||||
|
||||
interface Props {
|
||||
panel: PanelModel;
|
||||
@ -48,7 +48,7 @@ export const PanelOptionsTab: FC<Props> = ({
|
||||
};
|
||||
// Fist common panel settings Title, description
|
||||
elements.push(
|
||||
<OptionsGroup title="Panel settings" key="Panel settings">
|
||||
<OptionsGroup title="Panel settings" id="Panel settings" key="Panel settings">
|
||||
<Field label="Panel title">
|
||||
<Input defaultValue={panel.title} onBlur={e => onPanelConfigChange('title', e.currentTarget.value)} />
|
||||
</Field>
|
||||
@ -65,7 +65,7 @@ export const PanelOptionsTab: FC<Props> = ({
|
||||
);
|
||||
|
||||
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} />
|
||||
</OptionsGroup>
|
||||
);
|
||||
@ -73,7 +73,7 @@ export const PanelOptionsTab: FC<Props> = ({
|
||||
// Old legacy react editor
|
||||
if (plugin.editor && panel && !plugin.optionEditors) {
|
||||
elements.push(
|
||||
<OptionsGroup title="Display" key="legacy react editor">
|
||||
<OptionsGroup title="Display" id="legacy react editor" key="legacy react editor">
|
||||
<plugin.editor
|
||||
data={data}
|
||||
options={panel.getOptions()}
|
||||
@ -107,8 +107,9 @@ export const PanelOptionsTab: FC<Props> = ({
|
||||
renderTitle={isExpanded => (
|
||||
<>Panel links {!isExpanded && panelLinksCount > 0 && <Counter value={panelLinksCount} />}</>
|
||||
)}
|
||||
id="panel links"
|
||||
key="panel links"
|
||||
defaultToClosed={true}
|
||||
defaultToClosed
|
||||
>
|
||||
<DataLinksInlineEditor
|
||||
links={panel.links}
|
||||
@ -120,7 +121,7 @@ export const PanelOptionsTab: FC<Props> = ({
|
||||
);
|
||||
|
||||
elements.push(
|
||||
<OptionsGroup title="Panel repeats" key="panel repeats" defaultToClosed={true}>
|
||||
<OptionsGroup title="Panel repeats" id="panel repeats" key="panel repeats" defaultToClosed>
|
||||
<Field
|
||||
label="Repeat by variable"
|
||||
description="Repeat this panel for each value in the selected variable.
|
||||
|
Loading…
Reference in New Issue
Block a user