NewPanelEdit: General options categorisation (#23145)

* First bar gauge panel option

* Update doc comments

* Minor changes

* progress

* Minor type updates

* Fixing typing errors

* Fix that TS!

* Bring satisfaction to that beast called typescript

* Prototype

* Remove import

* Experimenting with different named categories

* Experimenting with category naming

* Naming is very hard

* merge master

* Remove commented code

* Fix merge

* Categorise panel options into collapsible sections

* Remove categories from table panel

Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
This commit is contained in:
Dominik Prokop
2020-04-09 21:22:43 +02:00
committed by GitHub
parent 278c312d58
commit 76827d2152
14 changed files with 155 additions and 57 deletions

View File

@@ -12,6 +12,8 @@ import { Forms, fieldMatchersUI, ValuePicker, useTheme } from '@grafana/ui';
import { getDataLinksVariableSuggestions } from '../../../panel/panellinks/link_srv';
import { OverrideEditor } from './OverrideEditor';
import { css } from 'emotion';
import groupBy from 'lodash/groupBy';
import { OptionsGroup } from './OptionsGroup';
interface Props {
plugin: PanelPlugin;
@@ -153,8 +155,14 @@ export const DefaultFieldConfigEditor: React.FC<Props> = ({ data, onChange, conf
: undefined
: (defaults as any)[item.path];
const label = (
<Forms.Label description={item.description} category={item.category?.slice(1)}>
{item.name}
</Forms.Label>
);
return (
<Forms.Field label={item.name} description={item.description} key={`${item.id}`}>
<Forms.Field label={label} key={`${item.id}/${item.isCustom}`}>
<item.editor
item={item}
value={value}
@@ -170,6 +178,21 @@ export const DefaultFieldConfigEditor: React.FC<Props> = ({ data, onChange, conf
[config]
);
// render all field configs
return <>{plugin.fieldConfigRegistry.list().map(renderEditor)}</>;
const groupedConfigs = groupBy(plugin.fieldConfigRegistry.list(), i => i.category && i.category[0]);
return (
<>
{Object.keys(groupedConfigs).map(k => {
return (
<OptionsGroup title={k}>
<>
{groupedConfigs[k].map(c => {
return renderEditor(c);
})}
</>
</OptionsGroup>
);
})}
</>
);
};

View File

@@ -16,10 +16,10 @@ export const OptionsGroup: FC<Props> = ({ title, children, defaultToClosed }) =>
return (
<div className={styles.box}>
<div className={styles.header} onClick={() => toggleExpand(!isExpanded)}>
{title}
<div className={cx(styles.toggle, 'editor-options-group-toggle')}>
<Icon name={isExpanded ? 'angle-down' : 'angle-left'} />
<Icon name={isExpanded ? 'angle-down' : 'angle-right'} />
</div>
{title}
</div>
{isExpanded && <div className={styles.body}>{children}</div>}
</div>
@@ -34,13 +34,13 @@ const getStyles = stylesFactory((theme: GrafanaTheme, isExpanded: boolean) => {
toggle: css`
color: ${theme.colors.textWeak};
font-size: ${theme.typography.size.lg};
margin-right: ${theme.spacing.sm};
`,
header: css`
display: flex;
cursor: pointer;
justify-content: space-between;
align-items: center;
padding: ${theme.spacing.sm} ${theme.spacing.md};
align-items: baseline;
padding: ${theme.spacing.sm} ${theme.spacing.md} ${theme.spacing.sm} ${theme.spacing.sm};
color: ${isExpanded ? theme.colors.text : theme.colors.formLabel};
font-weight: ${theme.typography.weight.semibold};
@@ -51,7 +51,7 @@ const getStyles = stylesFactory((theme: GrafanaTheme, isExpanded: boolean) => {
}
`,
body: css`
padding: 0 ${theme.spacing.md} ${theme.spacing.md} ${theme.spacing.md};
padding: 0 ${theme.spacing.md} ${theme.spacing.md} ${theme.spacing.xl};
`,
};
});

View File

@@ -2,18 +2,7 @@ import React, { useCallback, useState, CSSProperties } from 'react';
import Transition from 'react-transition-group/Transition';
import { FieldConfigSource, GrafanaTheme, PanelData, PanelPlugin, SelectableValue } from '@grafana/data';
import { DashboardModel, PanelModel } from '../../state';
import {
CustomScrollbar,
stylesFactory,
Tab,
TabContent,
TabsBar,
Select,
useTheme,
Container,
Icon,
Input,
} from '@grafana/ui';
import { CustomScrollbar, stylesFactory, Tab, TabContent, TabsBar, Select, useTheme, Icon, Input } from '@grafana/ui';
import { DefaultFieldConfigEditor, OverrideFieldConfigEditor } from './FieldConfigEditor';
import { css } from 'emotion';
import { PanelOptionsTab } from './PanelOptionsTab';
@@ -54,14 +43,12 @@ export const OptionsPaneContent: React.FC<{
}
return (
<Container padding="md">
<DefaultFieldConfigEditor
config={fieldConfig}
plugin={plugin}
onChange={onFieldConfigsChange}
data={data.series}
/>
</Container>
<DefaultFieldConfigEditor
config={fieldConfig}
plugin={plugin}
onChange={onFieldConfigsChange}
data={data.series}
/>
);
},
[data, plugin, panel, onFieldConfigsChange]

View File

@@ -1,7 +1,9 @@
import React, { useMemo } from 'react';
import { PanelOptionsEditorItem, PanelPlugin } from '@grafana/data';
import { set as lodashSet, get as lodashGet } from 'lodash';
import { PanelPlugin } from '@grafana/data';
import { Forms } from '@grafana/ui';
import groupBy from 'lodash/groupBy';
import { OptionsGroup } from './OptionsGroup';
interface PanelOptionsEditorProps<TOptions> {
plugin: PanelPlugin;
@@ -10,7 +12,11 @@ interface PanelOptionsEditorProps<TOptions> {
}
export const PanelOptionsEditor: React.FC<PanelOptionsEditorProps<any>> = ({ plugin, options, onChange }) => {
const optionEditors = useMemo(() => plugin.optionEditors, [plugin]);
const optionEditors = useMemo<Record<string, PanelOptionsEditorItem[]>>(() => {
return groupBy(plugin.optionEditors.list(), i => {
return i.category ? i.category[0] : 'Display';
});
}, [plugin]);
const onOptionChange = (key: string, value: any) => {
const newOptions = lodashSet({ ...options }, key, value);
@@ -19,16 +25,35 @@ export const PanelOptionsEditor: React.FC<PanelOptionsEditorProps<any>> = ({ plu
return (
<>
{optionEditors.list().map(e => {
if (e.showIf && !e.showIf(options)) {
return null;
}
{Object.keys(optionEditors).map(c => {
const optionsToShow = optionEditors[c]
.map((e, i) => {
if (e.showIf && !e.showIf(options)) {
return null;
}
return (
<Forms.Field label={e.name} description={e.description} key={e.id}>
<e.editor value={lodashGet(options, e.path)} onChange={value => onOptionChange(e.path, value)} item={e} />
</Forms.Field>
);
const label = (
<Forms.Label description={e.description} category={e.category?.slice(1)}>
{e.name}
</Forms.Label>
);
return (
<Forms.Field label={label} key={`${e.id}/i`}>
<e.editor
value={lodashGet(options, e.path)}
onChange={value => onOptionChange(e.path, value)}
item={e}
/>
</Forms.Field>
);
})
.filter(e => e !== null);
return optionsToShow.length > 0 ? (
<OptionsGroup title={c} defaultToClosed>
<div>{optionsToShow}</div>
</OptionsGroup>
) : null;
})}
</>
);

View File

@@ -73,14 +73,12 @@ export const PanelOptionsTab: FC<Props> = ({
if (plugin.optionEditors && panel) {
elements.push(
<OptionsGroup title="Display" key="panel plugin options">
<PanelOptionsEditor
key="panel options"
options={panel.getOptions()}
onChange={onPanelOptionsChanged}
plugin={plugin}
/>
</OptionsGroup>
<PanelOptionsEditor
key="panel options"
options={panel.getOptions()}
onChange={onPanelOptionsChanged}
plugin={plugin}
/>
);
}