diff --git a/packages/grafana-data/src/field/fieldColor.ts b/packages/grafana-data/src/field/fieldColor.ts index 7016c208994..78e0b632619 100644 --- a/packages/grafana-data/src/field/fieldColor.ts +++ b/packages/grafana-data/src/field/fieldColor.ts @@ -9,6 +9,8 @@ import { import stringHash from 'string-hash'; import tinycolor from 'tinycolor2'; +import { t } from '@grafana/i18n'; + import { getContrastRatio } from '../themes/colorManipulator'; import { GrafanaTheme2 } from '../themes/types'; import { reduceField } from '../transformations/fieldReducer'; @@ -30,10 +32,14 @@ export interface FieldColorMode extends RegistryItem { isContinuous?: boolean; isByValue?: boolean; useSeriesName?: boolean; + group?: string; } /** @internal */ export const fieldColorModeRegistry = new Registry(() => { + const accessibleGroup = t('grafana-data.field.fieldColor.accessibleGroup', 'Accessible'); + const otherGroup = t('grafana-data.field.fieldColor.otherGroup', 'Others'); + return [ { id: FieldColorModeId.Fixed, @@ -88,6 +94,7 @@ export const fieldColorModeRegistry = new Registry(() => { isContinuous: true, isByValue: true, interpolator: interpolateViridis, + group: accessibleGroup, }), new FieldColorSchemeMode({ id: FieldColorModeId.ContinuousMagma, @@ -95,6 +102,7 @@ export const fieldColorModeRegistry = new Registry(() => { isContinuous: true, isByValue: true, interpolator: interpolateMagma, + group: accessibleGroup, }), new FieldColorSchemeMode({ id: FieldColorModeId.ContinuousPlasma, @@ -102,6 +110,7 @@ export const fieldColorModeRegistry = new Registry(() => { isContinuous: true, isByValue: true, interpolator: interpolatePlasma, + group: accessibleGroup, }), new FieldColorSchemeMode({ id: FieldColorModeId.ContinuousInferno, @@ -109,6 +118,7 @@ export const fieldColorModeRegistry = new Registry(() => { isContinuous: true, isByValue: true, interpolator: interpolateInferno, + group: accessibleGroup, }), new FieldColorSchemeMode({ id: FieldColorModeId.ContinuousCividis, @@ -116,6 +126,7 @@ export const fieldColorModeRegistry = new Registry(() => { isContinuous: true, isByValue: true, interpolator: interpolateCividis, + group: accessibleGroup, }), new FieldColorSchemeMode({ id: FieldColorModeId.ContinuousGrYlRd, @@ -123,6 +134,7 @@ export const fieldColorModeRegistry = new Registry(() => { isContinuous: true, isByValue: true, getColors: (theme: GrafanaTheme2) => ['green', 'yellow', 'red'], + group: otherGroup, }), new FieldColorSchemeMode({ id: FieldColorModeId.ContinuousRdYlGr, @@ -130,6 +142,7 @@ export const fieldColorModeRegistry = new Registry(() => { isContinuous: true, isByValue: true, getColors: (theme: GrafanaTheme2) => ['red', 'yellow', 'green'], + group: otherGroup, }), new FieldColorSchemeMode({ id: FieldColorModeId.ContinuousBlYlRd, @@ -137,6 +150,7 @@ export const fieldColorModeRegistry = new Registry(() => { isContinuous: true, isByValue: true, getColors: (theme: GrafanaTheme2) => ['dark-blue', 'super-light-yellow', 'dark-red'], + group: otherGroup, }), new FieldColorSchemeMode({ id: FieldColorModeId.ContinuousYlRd, @@ -144,6 +158,7 @@ export const fieldColorModeRegistry = new Registry(() => { isContinuous: true, isByValue: true, getColors: (theme: GrafanaTheme2) => ['super-light-yellow', 'dark-red'], + group: otherGroup, }), new FieldColorSchemeMode({ id: FieldColorModeId.ContinuousBlPu, @@ -151,6 +166,7 @@ export const fieldColorModeRegistry = new Registry(() => { isContinuous: true, isByValue: true, getColors: (theme: GrafanaTheme2) => ['blue', 'purple'], + group: otherGroup, }), new FieldColorSchemeMode({ id: FieldColorModeId.ContinuousYlBl, @@ -158,6 +174,7 @@ export const fieldColorModeRegistry = new Registry(() => { isContinuous: true, isByValue: true, getColors: (theme: GrafanaTheme2) => ['super-light-yellow', 'dark-blue'], + group: otherGroup, }), new FieldColorSchemeMode({ id: FieldColorModeId.ContinuousBlues, @@ -165,6 +182,7 @@ export const fieldColorModeRegistry = new Registry(() => { isContinuous: true, isByValue: true, getColors: (theme: GrafanaTheme2) => ['panel-bg', 'dark-blue'], + group: otherGroup, }), new FieldColorSchemeMode({ id: FieldColorModeId.ContinuousReds, @@ -172,6 +190,7 @@ export const fieldColorModeRegistry = new Registry(() => { isContinuous: true, isByValue: true, getColors: (theme: GrafanaTheme2) => ['panel-bg', 'dark-red'], + group: otherGroup, }), new FieldColorSchemeMode({ id: FieldColorModeId.ContinuousGreens, @@ -179,6 +198,7 @@ export const fieldColorModeRegistry = new Registry(() => { isContinuous: true, isByValue: true, getColors: (theme: GrafanaTheme2) => ['panel-bg', 'dark-green'], + group: otherGroup, }), new FieldColorSchemeMode({ id: FieldColorModeId.ContinuousPurples, @@ -186,6 +206,7 @@ export const fieldColorModeRegistry = new Registry(() => { isContinuous: true, isByValue: true, getColors: (theme: GrafanaTheme2) => ['panel-bg', 'dark-purple'], + group: otherGroup, }), ]; }); @@ -197,6 +218,7 @@ interface BaseFieldColorSchemeModeOptions { isContinuous: boolean; isByValue: boolean; useSeriesName?: boolean; + group?: string; } interface FieldColorSchemeModeInterpolator extends BaseFieldColorSchemeModeOptions { @@ -222,6 +244,7 @@ export class FieldColorSchemeMode implements FieldColorMode { colorCacheTheme?: GrafanaTheme2; interpolator?: (value: number) => string; getNamedColors?: (theme: GrafanaTheme2) => string[]; + group?: string; constructor(options: FieldColorSchemeModeOptions) { this.id = options.id; @@ -232,6 +255,7 @@ export class FieldColorSchemeMode implements FieldColorMode { this.isByValue = options.isByValue; this.useSeriesName = options.useSeriesName; this.interpolator = options.interpolator; + this.group = options.group; } getColors(theme: GrafanaTheme2): string[] { diff --git a/public/app/core/components/OptionsUI/fieldColor.tsx b/public/app/core/components/OptionsUI/fieldColor.tsx index 39833b21c03..3f734a8e252 100644 --- a/public/app/core/components/OptionsUI/fieldColor.tsx +++ b/public/app/core/components/OptionsUI/fieldColor.tsx @@ -29,22 +29,39 @@ export const FieldColorEditor = ({ value, onChange, item, id }: Props) => { ? fieldColorModeRegistry.list() : fieldColorModeRegistry.list().filter((m) => !m.isByValue); - const options = availableOptions - .filter((mode) => !mode.excludeFromPicker) - .map((mode) => { - let suffix = mode.isByValue ? ' (by value)' : ''; + const filteredOptions = availableOptions.filter((option) => !option.excludeFromPicker); - return { - value: mode.id, - label: `${mode.name}${suffix}`, - description: mode.description, - isContinuous: mode.isContinuous, - isByValue: mode.isByValue, - component() { - return ; - }, - }; - }); + const options: Array> = []; + // collect any grouped options in this map + // this allows us to easily push to the child array without having to rescan the options array + // it also allows us to maintain group position in the order they're first encountered + const groupMap = new Map>>(); + + for (const option of filteredOptions) { + const suffix = option.isByValue ? ' (by value)' : ''; + + const groupName = option.group; + const selectOption = { + value: option.id, + label: `${option.name}${suffix}`, + description: option.description, + component() { + return ; + }, + }; + + if (groupName) { + let group = groupMap.get(groupName); + if (!group) { + group = []; + groupMap.set(groupName, group); + options.push({ label: groupName, options: group }); + } + group.push(selectOption); + } else { + options.push(selectOption); + } + } const onModeChange = (newMode: SelectableValue) => { onChange({ diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 1eb2c98e9ef..81d590746c5 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -8251,6 +8251,12 @@ "nextNYears_other": "Next {{count}} years" } }, + "field": { + "fieldColor": { + "accessibleGroup": "Accessible", + "otherGroup": "Others" + } + }, "valueFormats": { "categories": { "acceleration": {