From 76827d21528dfbba6e8fb6fa2ee0f8765349fbe2 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Thu, 9 Apr 2020 21:22:43 +0200 Subject: [PATCH] NewPanelEdit: General options categorisation (#23145) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 --- .../grafana-data/src/panel/PanelPlugin.ts | 1 + .../src/types/OptionsUIRegistryBuilder.ts | 1 + .../grafana-data/src/types/fieldOverrides.ts | 1 + packages/grafana-data/src/types/panel.ts | 1 + .../grafana-ui/src/components/Forms/Field.tsx | 16 ++++--- .../src/components/Forms/Label.story.tsx | 19 ++++++++ .../grafana-ui/src/components/Forms/Label.tsx | 29 +++++++++++- .../grafana-ui/src/utils/standardEditors.tsx | 13 ++++- .../PanelEditor/FieldConfigEditor.tsx | 29 ++++++++++-- .../components/PanelEditor/OptionsGroup.tsx | 12 ++--- .../PanelEditor/OptionsPaneContent.tsx | 27 +++-------- .../PanelEditor/PanelOptionsEditor.tsx | 47 ++++++++++++++----- .../PanelEditor/PanelOptionsTab.tsx | 14 +++--- scripts/circle-release-next-packages.sh | 2 +- 14 files changed, 155 insertions(+), 57 deletions(-) create mode 100644 packages/grafana-ui/src/components/Forms/Label.story.tsx diff --git a/packages/grafana-data/src/panel/PanelPlugin.ts b/packages/grafana-data/src/panel/PanelPlugin.ts index 092103ada26..d6c348d0d12 100644 --- a/packages/grafana-data/src/panel/PanelPlugin.ts +++ b/packages/grafana-data/src/panel/PanelPlugin.ts @@ -256,6 +256,7 @@ export class PanelPlugin ex path: (keyof TOptions & string) | string; editor: ComponentType; settings?: TSettings; + category?: string[]; defaultValue?: TValue; showIf?: (currentConfig: TOptions) => boolean; } diff --git a/packages/grafana-data/src/types/fieldOverrides.ts b/packages/grafana-data/src/types/fieldOverrides.ts index 82ebc708d01..34b4abe8cb9 100644 --- a/packages/grafana-data/src/types/fieldOverrides.ts +++ b/packages/grafana-data/src/types/fieldOverrides.ts @@ -57,6 +57,7 @@ export interface FieldConfigEditorConfig boolean; defaultValue?: TValue; diff --git a/packages/grafana-data/src/types/panel.ts b/packages/grafana-data/src/types/panel.ts index 527552e0c7b..6c964cd964e 100644 --- a/packages/grafana-data/src/types/panel.ts +++ b/packages/grafana-data/src/types/panel.ts @@ -123,6 +123,7 @@ export interface PanelOptionsEditorConfig boolean; } diff --git a/packages/grafana-ui/src/components/Forms/Field.tsx b/packages/grafana-ui/src/components/Forms/Field.tsx index 82e06830863..7a9405f1b69 100644 --- a/packages/grafana-ui/src/components/Forms/Field.tsx +++ b/packages/grafana-ui/src/components/Forms/Field.tsx @@ -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 = ({ // 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 + ); return (
- {label && ( - - )} + {labelElement}
{React.cloneElement(children, { invalid, disabled, loading })} {invalid && error && !horizontal && ( diff --git a/packages/grafana-ui/src/components/Forms/Label.story.tsx b/packages/grafana-ui/src/components/Forms/Label.story.tsx new file mode 100644 index 00000000000..b4b62f3e45d --- /dev/null +++ b/packages/grafana-ui/src/components/Forms/Label.story.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { Label } from './Label'; + +export default { + title: 'Forms/Label', + component: Label, +}; + +export const simple = () => { + return ; +}; + +export const categorised = () => { + return ( + + ); +}; diff --git a/packages/grafana-ui/src/components/Forms/Label.tsx b/packages/grafana-ui/src/components/Forms/Label.tsx index e47c523f2ba..fe5709943cc 100644 --- a/packages/grafana-ui/src/components/Forms/Label.tsx +++ b/packages/grafana-ui/src/components/Forms/Label.tsx @@ -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 { 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 = ({ children, description, className, ...labelProps }) => { +export const Label: React.FC = ({ children, description, className, category, ...labelProps }) => { const theme = useTheme(); const styles = getLabelStyles(theme); + const categories = category?.map(c => { + return ( + + {c} + + + ); + }); return (
diff --git a/packages/grafana-ui/src/utils/standardEditors.tsx b/packages/grafana-ui/src/utils/standardEditors.tsx index f0a26d0be90..df0d5d5f8a7 100644 --- a/packages/grafana-ui/src/utils/standardEditors.tsx +++ b/packages/grafana-ui/src/utils/standardEditors.tsx @@ -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 = { id: 'title', path: 'title', @@ -45,6 +46,7 @@ export const getStandardFieldConfigs = () => { expandTemplateVars: true, }, shouldApply: field => field.type !== FieldType.time, + category, }; const unit: FieldConfigPropertyItem = { @@ -62,6 +64,7 @@ export const getStandardFieldConfigs = () => { }, shouldApply: field => field.type === FieldType.number, + category, }; const min: FieldConfigPropertyItem = { @@ -78,6 +81,7 @@ export const getStandardFieldConfigs = () => { placeholder: 'auto', }, shouldApply: field => field.type === FieldType.number, + category, }; const max: FieldConfigPropertyItem = { @@ -95,6 +99,7 @@ export const getStandardFieldConfigs = () => { }, shouldApply: field => field.type === FieldType.number, + category, }; const decimals: FieldConfigPropertyItem = { @@ -115,6 +120,7 @@ export const getStandardFieldConfigs = () => { }, shouldApply: field => field.type === FieldType.number, + category, }; const thresholds: FieldConfigPropertyItem = { @@ -135,6 +141,7 @@ export const getStandardFieldConfigs = () => { ], }, shouldApply: field => field.type === FieldType.number, + category: ['Color & thresholds'], }; const mappings: FieldConfigPropertyItem = { @@ -149,6 +156,7 @@ export const getStandardFieldConfigs = () => { settings: {}, defaultValue: [], shouldApply: field => field.type === FieldType.number, + category: ['Value mappings'], }; const noValue: FieldConfigPropertyItem = { @@ -166,6 +174,7 @@ export const getStandardFieldConfigs = () => { }, // ??? any optionsUi with no value shouldApply: () => true, + category, }; const links: FieldConfigPropertyItem = { @@ -180,6 +189,7 @@ export const getStandardFieldConfigs = () => { placeholder: '-', }, shouldApply: () => true, + category: ['Data links'], }; const color: FieldConfigPropertyItem = { @@ -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]; }; /** diff --git a/public/app/features/dashboard/components/PanelEditor/FieldConfigEditor.tsx b/public/app/features/dashboard/components/PanelEditor/FieldConfigEditor.tsx index f4d88ab227b..09c2c65f7a1 100644 --- a/public/app/features/dashboard/components/PanelEditor/FieldConfigEditor.tsx +++ b/public/app/features/dashboard/components/PanelEditor/FieldConfigEditor.tsx @@ -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 = ({ data, onChange, conf : undefined : (defaults as any)[item.path]; + const label = ( + + {item.name} + + ); + return ( - + = ({ 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 ( + + <> + {groupedConfigs[k].map(c => { + return renderEditor(c); + })} + + + ); + })} + + ); }; diff --git a/public/app/features/dashboard/components/PanelEditor/OptionsGroup.tsx b/public/app/features/dashboard/components/PanelEditor/OptionsGroup.tsx index c3042c5cdc4..566efc1db79 100644 --- a/public/app/features/dashboard/components/PanelEditor/OptionsGroup.tsx +++ b/public/app/features/dashboard/components/PanelEditor/OptionsGroup.tsx @@ -16,10 +16,10 @@ export const OptionsGroup: FC = ({ title, children, defaultToClosed }) => return (
toggleExpand(!isExpanded)}> - {title}
- +
+ {title}
{isExpanded &&
{children}
}
@@ -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}; `, }; }); diff --git a/public/app/features/dashboard/components/PanelEditor/OptionsPaneContent.tsx b/public/app/features/dashboard/components/PanelEditor/OptionsPaneContent.tsx index 55215dca2ab..e74a88ed310 100644 --- a/public/app/features/dashboard/components/PanelEditor/OptionsPaneContent.tsx +++ b/public/app/features/dashboard/components/PanelEditor/OptionsPaneContent.tsx @@ -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 ( - - - + ); }, [data, plugin, panel, onFieldConfigsChange] diff --git a/public/app/features/dashboard/components/PanelEditor/PanelOptionsEditor.tsx b/public/app/features/dashboard/components/PanelEditor/PanelOptionsEditor.tsx index faae4176e81..6a4fd89a515 100644 --- a/public/app/features/dashboard/components/PanelEditor/PanelOptionsEditor.tsx +++ b/public/app/features/dashboard/components/PanelEditor/PanelOptionsEditor.tsx @@ -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 { plugin: PanelPlugin; @@ -10,7 +12,11 @@ interface PanelOptionsEditorProps { } export const PanelOptionsEditor: React.FC> = ({ plugin, options, onChange }) => { - const optionEditors = useMemo(() => plugin.optionEditors, [plugin]); + const optionEditors = useMemo>(() => { + 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> = ({ 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 ( - - onOptionChange(e.path, value)} item={e} /> - - ); + const label = ( + + {e.name} + + ); + return ( + + onOptionChange(e.path, value)} + item={e} + /> + + ); + }) + .filter(e => e !== null); + + return optionsToShow.length > 0 ? ( + +
{optionsToShow}
+
+ ) : null; })} ); diff --git a/public/app/features/dashboard/components/PanelEditor/PanelOptionsTab.tsx b/public/app/features/dashboard/components/PanelEditor/PanelOptionsTab.tsx index f471b67f08f..6bbacef6711 100644 --- a/public/app/features/dashboard/components/PanelEditor/PanelOptionsTab.tsx +++ b/public/app/features/dashboard/components/PanelEditor/PanelOptionsTab.tsx @@ -73,14 +73,12 @@ export const PanelOptionsTab: FC = ({ if (plugin.optionEditors && panel) { elements.push( - - - + ); } diff --git a/scripts/circle-release-next-packages.sh b/scripts/circle-release-next-packages.sh index 0ed54bd9b0d..a458a7b808a 100755 --- a/scripts/circle-release-next-packages.sh +++ b/scripts/circle-release-next-packages.sh @@ -70,7 +70,7 @@ else if [ "${CIRCLE_BRANCH}" == "master" ]; then exit_if_fail ./scripts/ci-metrics-publisher.sh "grafana.ci-buildtimes.$CIRCLE_JOB.$PACKAGE=$runtime" fi - + exit_status=$? if [ $exit_status -eq 0 ]; then unpublish_previous_canary "$PACKAGE"