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:
co-authored by
Torkel Ödegaard
parent
278c312d58
commit
76827d2152
@@ -256,6 +256,7 @@ export class PanelPlugin<TOptions = any, TFieldConfigOptions extends object = an
|
||||
|
||||
for (const customProp of builder.getRegistry().list()) {
|
||||
customProp.isCustom = true;
|
||||
customProp.category = ['Custom field options'].concat(customProp.category || []);
|
||||
// need to do something to make the custom items not conflict with standard ones
|
||||
// problem is id (registry index) is used as property path
|
||||
// so sort of need a property path on the FieldPropertyEditorItem
|
||||
|
||||
@@ -9,6 +9,7 @@ export interface OptionsEditorItem<TOptions, TSettings, TEditorProps, TValue> ex
|
||||
path: (keyof TOptions & string) | string;
|
||||
editor: ComponentType<TEditorProps>;
|
||||
settings?: TSettings;
|
||||
category?: string[];
|
||||
defaultValue?: TValue;
|
||||
showIf?: (currentConfig: TOptions) => boolean;
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ export interface FieldConfigEditorConfig<TOptions, TSettings = any, TValue = any
|
||||
path: (keyof TOptions & string) | string;
|
||||
name: string;
|
||||
description: string;
|
||||
category?: string[];
|
||||
settings?: TSettings;
|
||||
shouldApply?: (field: Field) => boolean;
|
||||
defaultValue?: TValue;
|
||||
|
||||
@@ -123,6 +123,7 @@ export interface PanelOptionsEditorConfig<TOptions, TSettings = any, TValue = an
|
||||
name: string;
|
||||
description: string;
|
||||
settings?: TSettings;
|
||||
category?: string[];
|
||||
defaultValue?: TValue;
|
||||
showIf?: (currentConfig: TOptions) => boolean;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ export interface FieldProps {
|
||||
/** Form input element, i.e Input or Switch */
|
||||
children: React.ReactElement;
|
||||
/** Label for the field */
|
||||
label?: string;
|
||||
label?: string | JSX.Element;
|
||||
/** Description of the field */
|
||||
description?: string;
|
||||
/** Indicates if field is in invalid state */
|
||||
@@ -71,14 +71,18 @@ export const Field: React.FC<FieldProps> = ({
|
||||
// Retrieve input's id to apply on the label for correct click interaction
|
||||
inputId = (child as React.ReactElement<{ id?: string }>).props.id;
|
||||
}
|
||||
const labelElement =
|
||||
typeof label === 'string' ? (
|
||||
<Label htmlFor={inputId} description={description}>
|
||||
{`${label}${required ? ' *' : ''}`}
|
||||
</Label>
|
||||
) : (
|
||||
label
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={cx(styles.field, horizontal && styles.fieldHorizontal, className)}>
|
||||
{label && (
|
||||
<Label htmlFor={inputId} description={description}>
|
||||
{`${label}${required ? ' *' : ''}`}
|
||||
</Label>
|
||||
)}
|
||||
{labelElement}
|
||||
<div>
|
||||
{React.cloneElement(children, { invalid, disabled, loading })}
|
||||
{invalid && error && !horizontal && (
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import { Label } from './Label';
|
||||
|
||||
export default {
|
||||
title: 'Forms/Label',
|
||||
component: Label,
|
||||
};
|
||||
|
||||
export const simple = () => {
|
||||
return <Label description="Opton description">Option name</Label>;
|
||||
};
|
||||
|
||||
export const categorised = () => {
|
||||
return (
|
||||
<Label category={['Category', 'Nested category']} description="Opton description">
|
||||
Option name
|
||||
</Label>
|
||||
);
|
||||
};
|
||||
@@ -2,10 +2,13 @@ import React from 'react';
|
||||
import { useTheme, stylesFactory } from '../../themes';
|
||||
import { GrafanaTheme } from '@grafana/data';
|
||||
import { css, cx } from 'emotion';
|
||||
import { Icon } from '../Icon/Icon';
|
||||
import tinycolor from 'tinycolor2';
|
||||
|
||||
export interface LabelProps extends React.LabelHTMLAttributes<HTMLLabelElement> {
|
||||
children: string;
|
||||
description?: string;
|
||||
category?: string[];
|
||||
}
|
||||
|
||||
export const getLabelStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||
@@ -23,18 +26,42 @@ export const getLabelStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||
color: ${theme.colors.formDescription};
|
||||
font-size: ${theme.typography.size.sm};
|
||||
font-weight: ${theme.typography.weight.regular};
|
||||
margin-top: ${theme.spacing.xxs};
|
||||
display: block;
|
||||
`,
|
||||
categories: css`
|
||||
color: ${theme.isLight
|
||||
? tinycolor(theme.colors.formLabel)
|
||||
.lighten(10)
|
||||
.toHexString()
|
||||
: tinycolor(theme.colors.formLabel)
|
||||
.darken(10)
|
||||
.toHexString()};
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
`,
|
||||
chevron: css`
|
||||
margin: 0 ${theme.spacing.xxs};
|
||||
`,
|
||||
};
|
||||
});
|
||||
|
||||
export const Label: React.FC<LabelProps> = ({ children, description, className, ...labelProps }) => {
|
||||
export const Label: React.FC<LabelProps> = ({ children, description, className, category, ...labelProps }) => {
|
||||
const theme = useTheme();
|
||||
const styles = getLabelStyles(theme);
|
||||
const categories = category?.map(c => {
|
||||
return (
|
||||
<span className={styles.categories}>
|
||||
<span>{c}</span>
|
||||
<Icon name="angle-right" className={styles.chevron} />
|
||||
</span>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={cx(styles.label, className)}>
|
||||
<label {...labelProps}>
|
||||
{categories}
|
||||
{children}
|
||||
{description && <span className={styles.description}>{description}</span>}
|
||||
</label>
|
||||
|
||||
@@ -32,6 +32,7 @@ import { StatsPickerEditor } from '../components/OptionsUI/stats';
|
||||
* Returns collection of common field config properties definitions
|
||||
*/
|
||||
export const getStandardFieldConfigs = () => {
|
||||
const category = ['Standard field options'];
|
||||
const title: FieldConfigPropertyItem<any, string, StringFieldConfigSettings> = {
|
||||
id: 'title',
|
||||
path: 'title',
|
||||
@@ -45,6 +46,7 @@ export const getStandardFieldConfigs = () => {
|
||||
expandTemplateVars: true,
|
||||
},
|
||||
shouldApply: field => field.type !== FieldType.time,
|
||||
category,
|
||||
};
|
||||
|
||||
const unit: FieldConfigPropertyItem<any, string, StringFieldConfigSettings> = {
|
||||
@@ -62,6 +64,7 @@ export const getStandardFieldConfigs = () => {
|
||||
},
|
||||
|
||||
shouldApply: field => field.type === FieldType.number,
|
||||
category,
|
||||
};
|
||||
|
||||
const min: FieldConfigPropertyItem<any, number, NumberFieldConfigSettings> = {
|
||||
@@ -78,6 +81,7 @@ export const getStandardFieldConfigs = () => {
|
||||
placeholder: 'auto',
|
||||
},
|
||||
shouldApply: field => field.type === FieldType.number,
|
||||
category,
|
||||
};
|
||||
|
||||
const max: FieldConfigPropertyItem<any, number, NumberFieldConfigSettings> = {
|
||||
@@ -95,6 +99,7 @@ export const getStandardFieldConfigs = () => {
|
||||
},
|
||||
|
||||
shouldApply: field => field.type === FieldType.number,
|
||||
category,
|
||||
};
|
||||
|
||||
const decimals: FieldConfigPropertyItem<any, number, NumberFieldConfigSettings> = {
|
||||
@@ -115,6 +120,7 @@ export const getStandardFieldConfigs = () => {
|
||||
},
|
||||
|
||||
shouldApply: field => field.type === FieldType.number,
|
||||
category,
|
||||
};
|
||||
|
||||
const thresholds: FieldConfigPropertyItem<any, ThresholdsConfig, ThresholdsFieldConfigSettings> = {
|
||||
@@ -135,6 +141,7 @@ export const getStandardFieldConfigs = () => {
|
||||
],
|
||||
},
|
||||
shouldApply: field => field.type === FieldType.number,
|
||||
category: ['Color & thresholds'],
|
||||
};
|
||||
|
||||
const mappings: FieldConfigPropertyItem<any, ValueMapping[], ValueMappingFieldConfigSettings> = {
|
||||
@@ -149,6 +156,7 @@ export const getStandardFieldConfigs = () => {
|
||||
settings: {},
|
||||
defaultValue: [],
|
||||
shouldApply: field => field.type === FieldType.number,
|
||||
category: ['Value mappings'],
|
||||
};
|
||||
|
||||
const noValue: FieldConfigPropertyItem<any, string, StringFieldConfigSettings> = {
|
||||
@@ -166,6 +174,7 @@ export const getStandardFieldConfigs = () => {
|
||||
},
|
||||
// ??? any optionsUi with no value
|
||||
shouldApply: () => true,
|
||||
category,
|
||||
};
|
||||
|
||||
const links: FieldConfigPropertyItem<any, DataLink[], StringFieldConfigSettings> = {
|
||||
@@ -180,6 +189,7 @@ export const getStandardFieldConfigs = () => {
|
||||
placeholder: '-',
|
||||
},
|
||||
shouldApply: () => true,
|
||||
category: ['Data links'],
|
||||
};
|
||||
|
||||
const color: FieldConfigPropertyItem<any, string, StringFieldConfigSettings> = {
|
||||
@@ -194,9 +204,10 @@ export const getStandardFieldConfigs = () => {
|
||||
placeholder: '-',
|
||||
},
|
||||
shouldApply: () => true,
|
||||
category: ['Color & thresholds'],
|
||||
};
|
||||
|
||||
return [unit, min, max, decimals, title, noValue, thresholds, mappings, links, color];
|
||||
return [unit, min, max, decimals, title, noValue, color, thresholds, mappings, links];
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user