grafana/public/app/features/dashboard/components/PanelEditor/DynamicConfigValueEditor.tsx

144 lines
3.6 KiB
TypeScript

import { css, cx } from '@emotion/css';
import React, { useId } from 'react';
import Highlighter from 'react-highlight-words';
import {
DynamicConfigValue,
FieldConfigOptionsRegistry,
FieldConfigProperty,
FieldOverrideContext,
GrafanaTheme2,
} from '@grafana/data';
import { Counter, Field, HorizontalGroup, IconButton, Label, useStyles2 } from '@grafana/ui';
import { OptionsPaneCategory } from './OptionsPaneCategory';
interface DynamicConfigValueEditorProps {
property: DynamicConfigValue;
registry: FieldConfigOptionsRegistry;
onChange: (value: DynamicConfigValue) => void;
context: FieldOverrideContext;
onRemove: () => void;
isSystemOverride?: boolean;
searchQuery: string;
}
export const DynamicConfigValueEditor = ({
property,
context,
registry,
onChange,
onRemove,
isSystemOverride,
searchQuery,
}: DynamicConfigValueEditorProps) => {
const styles = useStyles2(getStyles);
const item = registry?.getIfExists(property.id);
const componentId = useId();
if (!item) {
return null;
}
const isCollapsible =
Array.isArray(property.value) ||
property.id === FieldConfigProperty.Thresholds ||
property.id === FieldConfigProperty.Links ||
property.id === FieldConfigProperty.Mappings;
const labelCategory = item.category?.filter((c) => c !== item.name);
let editor;
/* eslint-disable react/display-name */
const renderLabel =
(includeDescription = true, includeCounter = false) =>
(isExpanded = false) =>
(
<HorizontalGroup justify="space-between">
<Label
category={labelCategory}
description={includeDescription ? item.description : undefined}
htmlFor={componentId}
>
<Highlighter
textToHighlight={item.name}
searchWords={[searchQuery]}
highlightClassName={'search-fragment-highlight'}
/>
{!isExpanded && includeCounter && item.getItemsCount && (
<Counter value={item.getItemsCount(property.value)} />
)}
</Label>
{!isSystemOverride && (
<div>
<IconButton name="times" onClick={onRemove} tooltip="Remove label" />
</div>
)}
</HorizontalGroup>
);
/* eslint-enable react/display-name */
if (isCollapsible) {
editor = (
<OptionsPaneCategory
id={item.name}
renderTitle={renderLabel(false, true)}
className={css`
padding-left: 0;
padding-right: 0;
`}
isNested
isOpenDefault={property.value !== undefined}
>
<item.override
value={property.value}
onChange={(value) => {
onChange(value);
}}
item={item}
context={context}
/>
</OptionsPaneCategory>
);
} else {
editor = (
<div>
<Field label={renderLabel()()} description={item.description}>
<item.override
value={property.value}
onChange={(value) => {
onChange(value);
}}
item={item}
context={context}
id={componentId}
/>
</Field>
</div>
);
}
return (
<div
className={cx(
isCollapsible && styles.collapsibleOverrideEditor,
!isCollapsible && 'dynamicConfigValueEditor--nonCollapsible'
)}
>
{editor}
</div>
);
};
const getStyles = (theme: GrafanaTheme2) => {
return {
collapsibleOverrideEditor: css`
label: collapsibleOverrideEditor;
& + .dynamicConfigValueEditor--nonCollapsible {
margin-top: ${theme.spacing(1)};
}
`,
};
};