mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Overrides: Show option group counters for options that represent collections (#23655)
This commit is contained in:
parent
4e521a84b4
commit
539edba31b
@ -41,6 +41,11 @@ export interface OptionsEditorItem<TOptions, TSettings, TEditorProps, TValue> ex
|
|||||||
* @param currentConfig Current options values
|
* @param currentConfig Current options values
|
||||||
*/
|
*/
|
||||||
showIf?: (currentConfig: TOptions) => boolean;
|
showIf?: (currentConfig: TOptions) => boolean;
|
||||||
|
/**
|
||||||
|
* Function that returns number of items if given option represents a collection, i.e. array of items.
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
getItemsCount?: (value?: TValue) => number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -143,6 +143,7 @@ export const getStandardFieldConfigs = () => {
|
|||||||
},
|
},
|
||||||
shouldApply: field => field.type === FieldType.number,
|
shouldApply: field => field.type === FieldType.number,
|
||||||
category: ['Color & thresholds'],
|
category: ['Color & thresholds'],
|
||||||
|
getItemsCount: value => (value ? value.steps.length : 0),
|
||||||
};
|
};
|
||||||
|
|
||||||
const mappings: FieldConfigPropertyItem<any, ValueMapping[], ValueMappingFieldConfigSettings> = {
|
const mappings: FieldConfigPropertyItem<any, ValueMapping[], ValueMappingFieldConfigSettings> = {
|
||||||
@ -158,6 +159,7 @@ export const getStandardFieldConfigs = () => {
|
|||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
shouldApply: field => field.type === FieldType.number,
|
shouldApply: field => field.type === FieldType.number,
|
||||||
category: ['Value mappings'],
|
category: ['Value mappings'],
|
||||||
|
getItemsCount: (value?) => (value ? value.length : 0),
|
||||||
};
|
};
|
||||||
|
|
||||||
const noValue: FieldConfigPropertyItem<any, string, StringFieldConfigSettings> = {
|
const noValue: FieldConfigPropertyItem<any, string, StringFieldConfigSettings> = {
|
||||||
@ -191,6 +193,7 @@ export const getStandardFieldConfigs = () => {
|
|||||||
},
|
},
|
||||||
shouldApply: () => true,
|
shouldApply: () => true,
|
||||||
category: ['Data links'],
|
category: ['Data links'],
|
||||||
|
getItemsCount: value => (value ? value.length : 0),
|
||||||
};
|
};
|
||||||
|
|
||||||
const color: FieldConfigPropertyItem<any, string, StringFieldConfigSettings> = {
|
const color: FieldConfigPropertyItem<any, string, StringFieldConfigSettings> = {
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { DynamicConfigValue, FieldConfigOptionsRegistry, FieldOverrideContext, GrafanaTheme } from '@grafana/data';
|
import { DynamicConfigValue, FieldConfigOptionsRegistry, FieldOverrideContext, GrafanaTheme } from '@grafana/data';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Field, HorizontalGroup, IconButton, IconName, Label, stylesFactory, useTheme } from '@grafana/ui';
|
import { Field, HorizontalGroup, IconButton, Label, stylesFactory, useTheme } from '@grafana/ui';
|
||||||
import { css, cx } from 'emotion';
|
import { css, cx } from 'emotion';
|
||||||
import { OptionsGroup } from './OptionsGroup';
|
import { OptionsGroup } from './OptionsGroup';
|
||||||
|
import { Counter } from '@grafana/ui/src/components/Tabs/Counter';
|
||||||
interface DynamicConfigValueEditorProps {
|
interface DynamicConfigValueEditorProps {
|
||||||
property: DynamicConfigValue;
|
property: DynamicConfigValue;
|
||||||
registry: FieldConfigOptionsRegistry;
|
registry: FieldConfigOptionsRegistry;
|
||||||
@ -28,11 +29,15 @@ export const DynamicConfigValueEditor: React.FC<DynamicConfigValueEditorProps> =
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
let editor;
|
let editor;
|
||||||
const renderLabel = (iconName: IconName, includeDescription = true) => () => (
|
|
||||||
|
const renderLabel = (includeDescription = true, includeCounter = false) => (isExpanded = false) => (
|
||||||
<HorizontalGroup justify="space-between">
|
<HorizontalGroup justify="space-between">
|
||||||
<Label description={includeDescription ? item.description : undefined}>{item.name}</Label>
|
<Label description={includeDescription ? item.description : undefined}>
|
||||||
|
{item.name}
|
||||||
|
{!isExpanded && includeCounter && item.getItemsCount && <Counter value={item.getItemsCount(property.value)} />}
|
||||||
|
</Label>
|
||||||
<div>
|
<div>
|
||||||
<IconButton name={iconName} onClick={onRemove} />
|
<IconButton name="times" onClick={onRemove} />
|
||||||
</div>
|
</div>
|
||||||
</HorizontalGroup>
|
</HorizontalGroup>
|
||||||
);
|
);
|
||||||
@ -40,7 +45,7 @@ export const DynamicConfigValueEditor: React.FC<DynamicConfigValueEditorProps> =
|
|||||||
if (isCollapsible) {
|
if (isCollapsible) {
|
||||||
editor = (
|
editor = (
|
||||||
<OptionsGroup
|
<OptionsGroup
|
||||||
renderTitle={renderLabel('trash-alt', false)}
|
renderTitle={renderLabel(false, true)}
|
||||||
className={css`
|
className={css`
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
@ -61,7 +66,7 @@ export const DynamicConfigValueEditor: React.FC<DynamicConfigValueEditorProps> =
|
|||||||
} else {
|
} else {
|
||||||
editor = (
|
editor = (
|
||||||
<div>
|
<div>
|
||||||
<Field label={renderLabel('times')()} description={item.description}>
|
<Field label={renderLabel()()} description={item.description}>
|
||||||
<item.override
|
<item.override
|
||||||
value={property.value}
|
value={property.value}
|
||||||
onChange={value => {
|
onChange={value => {
|
||||||
|
@ -13,6 +13,7 @@ import { getDataLinksVariableSuggestions } from '../../../panel/panellinks/link_
|
|||||||
import { OverrideEditor } from './OverrideEditor';
|
import { OverrideEditor } from './OverrideEditor';
|
||||||
import groupBy from 'lodash/groupBy';
|
import groupBy from 'lodash/groupBy';
|
||||||
import { OptionsGroup } from './OptionsGroup';
|
import { OptionsGroup } from './OptionsGroup';
|
||||||
|
import { Counter } from '@grafana/ui/src/components/Tabs/Counter';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
plugin: PanelPlugin;
|
plugin: PanelPlugin;
|
||||||
@ -180,8 +181,19 @@ export const DefaultFieldConfigEditor: React.FC<Props> = ({ data, onChange, conf
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{Object.keys(groupedConfigs).map((k, i) => {
|
{Object.keys(groupedConfigs).map((k, i) => {
|
||||||
|
const groupItemsCounter = countGroupItems(groupedConfigs[k], config);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<OptionsGroup title={k} key={`${k}/${i}`}>
|
<OptionsGroup
|
||||||
|
renderTitle={isExpanded => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{k} {!isExpanded && groupItemsCounter && <Counter value={groupItemsCounter} />}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
key={`${k}/${i}`}
|
||||||
|
>
|
||||||
<>
|
<>
|
||||||
{groupedConfigs[k].map(c => {
|
{groupedConfigs[k].map(c => {
|
||||||
return renderEditor(c);
|
return renderEditor(c);
|
||||||
@ -193,3 +205,20 @@ export const DefaultFieldConfigEditor: React.FC<Props> = ({ data, onChange, conf
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const countGroupItems = (group: FieldConfigPropertyItem[], config: FieldConfigSource) => {
|
||||||
|
let counter = 0;
|
||||||
|
|
||||||
|
for (const item of group) {
|
||||||
|
const value = item.isCustom
|
||||||
|
? config.defaults.custom
|
||||||
|
? config.defaults.custom[item.path]
|
||||||
|
: undefined
|
||||||
|
: (config.defaults as any)[item.path];
|
||||||
|
if (item.getItemsCount && item.getItemsCount(value) > 0) {
|
||||||
|
counter = counter + item.getItemsCount(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return counter === 0 ? undefined : counter;
|
||||||
|
};
|
||||||
|
@ -91,6 +91,7 @@ export const OptionsPaneContent: React.FC<{
|
|||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
setSearchMode={setSearchMode}
|
setSearchMode={setSearchMode}
|
||||||
setActiveTab={setActiveTab}
|
setActiveTab={setActiveTab}
|
||||||
|
panel={panel}
|
||||||
/>
|
/>
|
||||||
</TabsBar>
|
</TabsBar>
|
||||||
<TabContent className={styles.tabContent}>
|
<TabContent className={styles.tabContent}>
|
||||||
@ -128,7 +129,11 @@ export const TabsBarContent: React.FC<{
|
|||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
setSearchMode: (mode: boolean) => void;
|
setSearchMode: (mode: boolean) => void;
|
||||||
setActiveTab: (tab: string) => void;
|
setActiveTab: (tab: string) => void;
|
||||||
}> = ({ width, showFields, isSearching, activeTab, onClose, setSearchMode, setActiveTab, styles }) => {
|
panel: PanelModel;
|
||||||
|
}> = ({ width, showFields, isSearching, activeTab, onClose, setSearchMode, setActiveTab, styles, panel }) => {
|
||||||
|
const overridesCount =
|
||||||
|
panel.getFieldConfig().overrides.length === 0 ? undefined : panel.getFieldConfig().overrides.length;
|
||||||
|
|
||||||
if (isSearching) {
|
if (isSearching) {
|
||||||
const defaultStyles = {
|
const defaultStyles = {
|
||||||
transition: 'width 50ms ease-in-out',
|
transition: 'width 50ms ease-in-out',
|
||||||
@ -190,6 +195,7 @@ export const TabsBarContent: React.FC<{
|
|||||||
<Tab
|
<Tab
|
||||||
key={item.value}
|
key={item.value}
|
||||||
label={item.label}
|
label={item.label}
|
||||||
|
counter={item.value === 'overrides' ? overridesCount : undefined}
|
||||||
active={active.value === item.value}
|
active={active.value === item.value}
|
||||||
onChangeTab={() => setActiveTab(item.value)}
|
onChangeTab={() => setActiveTab(item.value)}
|
||||||
/>
|
/>
|
||||||
|
@ -7,6 +7,7 @@ import { getPanelLinksVariableSuggestions } from '../../../panel/panellinks/link
|
|||||||
import { getVariables } from '../../../variables/state/selectors';
|
import { getVariables } from '../../../variables/state/selectors';
|
||||||
import { PanelOptionsEditor } from './PanelOptionsEditor';
|
import { PanelOptionsEditor } from './PanelOptionsEditor';
|
||||||
import { AngularPanelOptions } from '../../panel_editor/AngularPanelOptions';
|
import { AngularPanelOptions } from '../../panel_editor/AngularPanelOptions';
|
||||||
|
import { Counter } from '@grafana/ui/src/components/Tabs/Counter';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
panel: PanelModel;
|
panel: PanelModel;
|
||||||
@ -29,6 +30,7 @@ export const PanelOptionsTab: FC<Props> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const elements: JSX.Element[] = [];
|
const elements: JSX.Element[] = [];
|
||||||
const linkVariablesSuggestions = useMemo(() => getPanelLinksVariableSuggestions(), []);
|
const linkVariablesSuggestions = useMemo(() => getPanelLinksVariableSuggestions(), []);
|
||||||
|
const panelLinksCount = panel && panel.links ? panel.links.length : undefined;
|
||||||
|
|
||||||
const variableOptions = getVariableOptions();
|
const variableOptions = getVariableOptions();
|
||||||
const directionOptions = [
|
const directionOptions = [
|
||||||
@ -89,7 +91,15 @@ export const PanelOptionsTab: FC<Props> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
elements.push(
|
elements.push(
|
||||||
<OptionsGroup title="Panel links" key="panel links" defaultToClosed={true}>
|
<OptionsGroup
|
||||||
|
renderTitle={isExpanded => (
|
||||||
|
<>
|
||||||
|
Panel links {!isExpanded && panelLinksCount && panelLinksCount !== 0 && <Counter value={panelLinksCount} />}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
key="panel links"
|
||||||
|
defaultToClosed={true}
|
||||||
|
>
|
||||||
<DataLinksInlineEditor
|
<DataLinksInlineEditor
|
||||||
links={panel.links}
|
links={panel.links}
|
||||||
onChange={links => onPanelConfigChange('links', links)}
|
onChange={links => onPanelConfigChange('links', links)}
|
||||||
|
Loading…
Reference in New Issue
Block a user