diff --git a/enterprise/frontend/src/metabase-enterprise/whitelabel/components/ChartColorSettings/ChartColorSettings.tsx b/enterprise/frontend/src/metabase-enterprise/whitelabel/components/ChartColorSettings/ChartColorSettings.tsx index 5522f2d7f023b7baf7d5e4592a9d74f7b6fcbd93..8b41e2824900d5b6b6ce57593958874b118417ba 100644 --- a/enterprise/frontend/src/metabase-enterprise/whitelabel/components/ChartColorSettings/ChartColorSettings.tsx +++ b/enterprise/frontend/src/metabase-enterprise/whitelabel/components/ChartColorSettings/ChartColorSettings.tsx @@ -78,13 +78,12 @@ const ChartColorTable = ({ <TableBody> {colorGroups.map((colorGroup, index) => ( <TableBodyRow key={index}> - {colorGroup.map((colorName, index) => ( + {colorGroup.map(colorName => ( <ChartColorCell key={colorName} color={colors[colorName]} originalColor={color(colorName, colorPalette)} colorName={colorName} - isAccent={index === 0} onChange={onChange} /> ))} @@ -99,7 +98,6 @@ interface ChartColorCellProps { color?: string; originalColor: string; colorName: string; - isAccent: boolean; onChange: (colorName: string, color?: string) => void; } @@ -107,7 +105,6 @@ const ChartColorCell = memo(function ChartColorCell({ color, originalColor, colorName, - isAccent, onChange, }: ChartColorCellProps) { const handleChange = useCallback( @@ -121,8 +118,8 @@ const ChartColorCell = memo(function ChartColorCell({ <TableBodyCell> <ColorPicker value={color ?? originalColor} - placeholder={isAccent ? originalColor : t`Auto`} - isAuto={color == null && !isAccent} + placeholder={t`Auto`} + isAuto={color == null} onChange={handleChange} /> </TableBodyCell> diff --git a/frontend/src/metabase/components/form/widgets/FormColorWidget.jsx b/frontend/src/metabase/components/form/widgets/FormColorWidget.jsx index 7f017e8f40280d2ab5d169e3dadb47fa7f7beac0..2a029bc2b3217b48d529dabdcf8d62b5e920a1b6 100644 --- a/frontend/src/metabase/components/form/widgets/FormColorWidget.jsx +++ b/frontend/src/metabase/components/form/widgets/FormColorWidget.jsx @@ -1,7 +1,7 @@ /* eslint-disable react/prop-types */ import React from "react"; -import { getForegroundColors } from "metabase/lib/colors/groups"; +import { getDistinctColors } from "metabase/lib/colors/groups"; import ColorSelector from "metabase/core/components/ColorSelector"; const FormColorWidget = ({ field, initial }) => ( @@ -9,7 +9,7 @@ const FormColorWidget = ({ field, initial }) => ( <ColorSelector {...field} value={field.value || initial()} - colors={getForegroundColors()} + colors={getDistinctColors()} /> </div> ); diff --git a/frontend/src/metabase/lib/colors/charts.ts b/frontend/src/metabase/lib/colors/charts.ts index c316e983720918915d3021a4025271ecb6db6f6b..7793e90faadc0cbf90a0b353effc35cb2121ffcf 100644 --- a/frontend/src/metabase/lib/colors/charts.ts +++ b/frontend/src/metabase/lib/colors/charts.ts @@ -15,7 +15,7 @@ export const getColorsForValues = ( } else { return getOrderBasedMapping( keys, - getHarmonyColors(), + getHarmonyColors({ light: keys.length > ACCENT_COUNT * 2 }), existingMapping, getPreferredColor, ); diff --git a/frontend/src/metabase/lib/colors/charts.unit.spec.ts b/frontend/src/metabase/lib/colors/charts.unit.spec.ts index 3998ad3be21ed863bb508a7f4b2675cad2d89383..02f580c672ae749e4a52b5c075d426c74be83ca0 100644 --- a/frontend/src/metabase/lib/colors/charts.unit.spec.ts +++ b/frontend/src/metabase/lib/colors/charts.unit.spec.ts @@ -10,9 +10,9 @@ describe("charts", () => { const newMapping = getColorsForValues(keys, existingMapping); expect(newMapping).toEqual({ - count: color("accent1"), - profit: color("success"), - sum_2: color("accent6"), + count: color("accent1"), // existing colors are not changed + profit: color("success"), // a preferred color + sum_2: color("accent6"), // only accent colors are used for other keys }); }); @@ -23,10 +23,10 @@ describe("charts", () => { const newMapping = getColorsForValues(keys, existingMapping); expect(newMapping).toEqual({ - count: color("accent1"), - profit: color("success"), - distinct: color("accent4"), - sum_2: color("accent6"), + count: color("accent1"), // existing colors are not changed + profit: color("success"), // a preferred color + distinct: color("accent4"), // some color based on the hash + sum_2: color("accent6"), // the same color is used despite different keys count }); }); @@ -37,14 +37,14 @@ describe("charts", () => { const newMapping = getColorsForValues(keys, existingMapping); expect(newMapping).toMatchObject({ - count: color("accent1"), - sum: color("accent0"), - profit: color("success"), - S0: color("accent0-light"), - S1: color("accent0-dark"), - S2: color("accent1-light"), - S3: color("accent1-dark"), - S4: color("accent2"), + count: color("accent1"), // existing colors are not changed + sum: color("accent0"), // a color from the palette because accent1 would be preferred, but it's already used + profit: color("success"), // a preferred color + S0: color("accent0-dark"), // the next color from palette + S1: color("accent1-dark"), // only dark accents are used when there are <= 16 keys + S2: color("accent2"), + S3: color("accent2-dark"), + S4: color("accent3"), }); }); @@ -55,15 +55,15 @@ describe("charts", () => { const newMapping = getColorsForValues(keys, existingMapping); expect(newMapping).toMatchObject({ - count: color("accent1"), - sum: color("accent0"), - profit: color("success"), - S0: color("accent0-light"), - S1: color("accent0-dark"), + count: color("accent1"), // existing colors are not changed + sum: color("accent0"), // a color from the palette because accent1 would be preferred, but it's already used + profit: color("success"), // a preferred color + S0: color("accent0-light"), // the next color from palette + S1: color("accent0-dark"), // both light and dark accents are used when there are > 16 series S2: color("accent1-light"), S3: color("accent1-dark"), S4: color("accent2"), - S28: color("accent2"), + S28: color("accent2"), // we have 24 colors in the palette, that's why they would repeat after 24 keys }); }); }); diff --git a/frontend/src/metabase/lib/colors/groups.ts b/frontend/src/metabase/lib/colors/groups.ts index 5066e51bfb4882cb0bca58c0b8690da84ba20fc5..c60e91495e1f58587eefdf430c3fd6c41ea3ce8c 100644 --- a/frontend/src/metabase/lib/colors/groups.ts +++ b/frontend/src/metabase/lib/colors/groups.ts @@ -1,5 +1,6 @@ import { times, unzip } from "lodash"; import { ACCENT_COUNT, color } from "./palette"; +import { ColorGroupOptions } from "./types"; export const getAccentColors = () => { return times(ACCENT_COUNT, i => color(`accent${i}`)); @@ -13,16 +14,23 @@ export const getShadeColors = () => { return times(ACCENT_COUNT, i => color(`accent${i}-dark`)); }; -export const getForegroundColors = () => { - return [...getAccentColors(), ...getTintColors(), ...getShadeColors()]; +export const getDistinctColors = (options?: ColorGroupOptions) => { + return getAccentColorRanges(options).flat(); }; -export const getBackgroundColors = () => { - return [...getAccentColors(), ...getTintColors()]; +export const getHarmonyColors = (options?: ColorGroupOptions) => { + return unzip(getAccentColorRanges(options)).flat(); }; -export const getHarmonyColors = () => { - return unzip([getAccentColors(), getTintColors(), getShadeColors()]).flat(); +export const getAccentColorRanges = ({ + light = true, + dark = true, +}: ColorGroupOptions = {}) => { + const ranges = [getAccentColors()]; + light && ranges.push(getTintColors()); + dark && ranges.push(getShadeColors()); + + return ranges; }; export const getStatusColorRanges = () => { diff --git a/frontend/src/metabase/lib/colors/types.ts b/frontend/src/metabase/lib/colors/types.ts index c6a2a614061a3ac2323c6239041c4e0fa0e0bfa9..12fe1f1e770e0c612af55de641143233cdd89efa 100644 --- a/frontend/src/metabase/lib/colors/types.ts +++ b/frontend/src/metabase/lib/colors/types.ts @@ -1 +1,6 @@ export type ColorPalette = Record<string, string>; + +export interface ColorGroupOptions { + light?: boolean; + dark?: boolean; +} diff --git a/frontend/src/metabase/visualizations/components/LegendHeader.jsx b/frontend/src/metabase/visualizations/components/LegendHeader.jsx index 32879d87b5b089590b658b29986d8a3f18278a30..e60dbc8330b18450103e1078ae99f1733b1da8e9 100644 --- a/frontend/src/metabase/visualizations/components/LegendHeader.jsx +++ b/frontend/src/metabase/visualizations/components/LegendHeader.jsx @@ -2,13 +2,13 @@ import React, { Component } from "react"; import PropTypes from "prop-types"; import cx from "classnames"; -import { getForegroundColors } from "metabase/lib/colors/groups"; +import { getDistinctColors } from "metabase/lib/colors/groups"; import Icon, { iconPropTypes } from "metabase/components/Icon"; import ExplicitSize from "../../components/ExplicitSize"; import LegendItem from "./LegendItem"; import styles from "./Legend.css"; -const DEFAULT_COLORS = getForegroundColors(); +const DEFAULT_COLORS = getDistinctColors(); const MIN_WIDTH_PER_SERIES = 100; class LegendHeader extends Component { diff --git a/frontend/src/metabase/visualizations/components/LineAreaBarChart.jsx b/frontend/src/metabase/visualizations/components/LineAreaBarChart.jsx index db6ec0446c8667e8324fdec24de416fd732aeff0..1ff0fd7d889151469ec9f6ce9a79a7be1af4c0a5 100644 --- a/frontend/src/metabase/visualizations/components/LineAreaBarChart.jsx +++ b/frontend/src/metabase/visualizations/components/LineAreaBarChart.jsx @@ -74,7 +74,7 @@ for (let i = 0; i < MAX_SERIES; i++) { addCSSRule(`.LineAreaBarChart.mute-${i} svg:not(.stacked) .row`, MUTE_STYLE); } -import { getForegroundColors } from "metabase/lib/colors/groups"; +import { getDistinctColors } from "metabase/lib/colors/groups"; export default class LineAreaBarChart extends Component { static noHeader = true; @@ -297,7 +297,7 @@ export default class LineAreaBarChart extends Component { : series.map(single => single.card.name); const colors = seriesSettings ? seriesSettings.map(s => s.color) - : Object.values(getForegroundColors()); + : Object.values(getDistinctColors()); return { title, diff --git a/frontend/src/metabase/visualizations/components/settings/ChartNestedSettingSeries.jsx b/frontend/src/metabase/visualizations/components/settings/ChartNestedSettingSeries.jsx index b11afd99d2dd4a3c0784e13e83326cf5000d4163..fc22688e6b1d43b3151ddbe493decfc9c94eb33a 100644 --- a/frontend/src/metabase/visualizations/components/settings/ChartNestedSettingSeries.jsx +++ b/frontend/src/metabase/visualizations/components/settings/ChartNestedSettingSeries.jsx @@ -1,7 +1,7 @@ /* eslint-disable react/prop-types */ import React from "react"; -import { getForegroundColors } from "metabase/lib/colors/groups"; +import { getDistinctColors } from "metabase/lib/colors/groups"; import ColorSelector from "metabase/core/components/ColorSelector"; import { SegmentedControl } from "metabase/components/SegmentedControl"; import Icon from "metabase/components/Icon"; @@ -42,7 +42,7 @@ export default class ChartNestedSettingSeries extends React.Component { <div className="flex align-center"> <ColorSelector value={settings.color} - colors={getForegroundColors()} + colors={getDistinctColors()} onChange={value => onChangeObjectSettings(single, { color: value }) } diff --git a/frontend/src/metabase/visualizations/components/settings/ChartSettingColorPicker.jsx b/frontend/src/metabase/visualizations/components/settings/ChartSettingColorPicker.jsx index defb14bdffbf41c1d29f2f57a23f2d3d26b787ca..57f0de4d26a29451c13f1ae35b0e7d483d748921 100644 --- a/frontend/src/metabase/visualizations/components/settings/ChartSettingColorPicker.jsx +++ b/frontend/src/metabase/visualizations/components/settings/ChartSettingColorPicker.jsx @@ -1,7 +1,7 @@ /* eslint-disable react/prop-types */ import React from "react"; -import { getForegroundColors } from "metabase/lib/colors/groups"; +import { getDistinctColors } from "metabase/lib/colors/groups"; import ColorSelector from "metabase/core/components/ColorSelector"; export default function ChartSettingColorPicker(props) { @@ -11,7 +11,7 @@ export default function ChartSettingColorPicker(props) { <div className="flex align-center mb1"> <ColorSelector value={value} - colors={getForegroundColors()} + colors={getDistinctColors()} onChange={onChange} /> {props.title && <h4 className="ml1">{props.title}</h4>} diff --git a/frontend/src/metabase/visualizations/components/settings/ChartSettingGaugeSegments.jsx b/frontend/src/metabase/visualizations/components/settings/ChartSettingGaugeSegments.jsx index 1c8643043adb8cd88adccdeb73d25a9da2f7600d..18fa6a354364840ad7c8a4d72cde2293f4d58798 100644 --- a/frontend/src/metabase/visualizations/components/settings/ChartSettingGaugeSegments.jsx +++ b/frontend/src/metabase/visualizations/components/settings/ChartSettingGaugeSegments.jsx @@ -5,7 +5,7 @@ import { t } from "ttag"; import _ from "underscore"; import { color } from "metabase/lib/colors"; -import { getForegroundColors } from "metabase/lib/colors/groups"; +import { getDistinctColors } from "metabase/lib/colors/groups"; import ColorSelector from "metabase/core/components/ColorSelector"; import Button from "metabase/core/components/Button"; @@ -101,7 +101,7 @@ const ChartSettingGaugeSegments = ({ value: segments, onChange }) => { function getColorPalette() { return [ - ...getForegroundColors(), + ...getDistinctColors(), color("error"), color("warning"), color("success"), diff --git a/frontend/src/metabase/visualizations/components/settings/ChartSettingsTableFormatting.jsx b/frontend/src/metabase/visualizations/components/settings/ChartSettingsTableFormatting.jsx index 867adf1d636543134cd2db5cd42a95dd9d3be785..b64755f180feeb18cff9f02e39d6035bb32ca890 100644 --- a/frontend/src/metabase/visualizations/components/settings/ChartSettingsTableFormatting.jsx +++ b/frontend/src/metabase/visualizations/components/settings/ChartSettingsTableFormatting.jsx @@ -4,7 +4,7 @@ import React from "react"; import { t, jt } from "ttag"; import { - getBackgroundColors, + getDistinctColors, getStatusColorRanges, } from "metabase/lib/colors/groups"; @@ -57,7 +57,7 @@ export const ALL_OPERATOR_NAMES = { }; // TODO -const COLORS = getBackgroundColors(); +const COLORS = getDistinctColors({ dark: false }); const COLOR_RANGES = getStatusColorRanges(); const DEFAULTS_BY_TYPE = { diff --git a/frontend/src/metabase/visualizations/visualizations/Map.jsx b/frontend/src/metabase/visualizations/visualizations/Map.jsx index 3930c66aa705f14faf8e8f417cde18ddc0d7d92e..1fb2d611587a4e1cd6da270a9ad888a09ee4c935 100644 --- a/frontend/src/metabase/visualizations/visualizations/Map.jsx +++ b/frontend/src/metabase/visualizations/visualizations/Map.jsx @@ -31,7 +31,7 @@ import _ from "underscore"; const PIN_MAP_TYPES = new Set(["pin", "heat", "grid"]); -import { getForegroundColors } from "metabase/lib/colors/groups"; +import { getDistinctColors } from "metabase/lib/colors/groups"; import ColorRangeSelector from "metabase/core/components/ColorRangeSelector"; export default class Map extends Component { @@ -257,16 +257,16 @@ export default class Map extends Component { title: t`Color`, widget: ColorRangeSelector, props: { - colors: getForegroundColors(), + colors: getDistinctColors(), colorMapping: Object.fromEntries( - getForegroundColors().map(color => [ + getDistinctColors().map(color => [ color, getColorplethColorScale(color), ]), ), isQuantile: true, }, - default: getColorplethColorScale(getForegroundColors()[0]), + default: getColorplethColorScale(getDistinctColors()[0]), getHidden: (series, vizSettings) => vizSettings["map.type"] !== "region", }, "map.zoom": {},