Skip to content

Conversation

@Facyla
Copy link

@Facyla Facyla commented Dec 18, 2025

User description

SUMMARY

Add a JSON-based customization block that allows to override linearColorScheme colors and thresholds with custom thresholds and colors.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

image

TESTING INSTRUCTIONS

  • Paste a sample customization in "Custom Color Scale": the linearScheme is overrided, resulting in the customized Color Scheme to apply.
    [ { "percent": 0, "color": "white" }, { "percent": 0.01, "color": "#FF0000AA" }, { "percent": 20, "color": "#FA0" }, { "percent": 50, "color": "#FF0" }, { "percent": 65, "color": "#9F0" }, { "percent": 99.99, "color": "#000A" }, { "percent": 100, "color": "black" } ]
    The inline help provides a sample configuration that can also be copied/pasted (with some cleaning and lines returns).

  • Edit sample values to adjust to the map needs: the changes appear immediately on map

    • update thresholds with values in percentage; decimal values allowed (eg. 0.001): the color will be applied from that lower limit up to the next one
    • adjust colors, using any valid CSS color, including named colors (red, green, etc.), rgb and hex codes, with or without transparency. A color selector helps visually choosing the color value
    • add or remove thresholds to fit your needs
  • The provided configuration requires a full valid JSON, including brackets, commas except for the last line, quoted keys and quoted values for color codes.

ADDITIONAL INFORMATION

  • Has associated issue: Implement custom formatting similar to Table Chart into Country Map plugin #34427
  • Required feature flags:
  • Changes UI: add a new control
  • Includes DB Migration (follow approval process in SIP-59)
    • Migration is atomic, supports rollback & is backwards-compatible
    • Confirm DB migration upgrade and downgrade tested
    • Runtime estimates and downtime expectations provided
  • Introduces new feature or API
  • Removes existing feature or API

CodeAnt-AI Description

Allow custom percent-based color scales for CountryMap and add a color picker

What Changed

  • Users can paste a JSON list of percent/color entries in a new "Custom Color Scale (by %)" textarea to override the map's linear color scheme; the map immediately uses those thresholds and colors.
  • A new color picker control provides an easy way to pick a color and copy its HEX into the custom scale.
  • The chart now tolerates malformed or missing inputs: invalid JSON, invalid colors, or non-numeric metrics fall back to safe defaults and won't break rendering; identical metric values map to a central color.
  • If no valid custom scale is provided, the existing linear color scheme is used as before.

Impact

✅ Clearer thematic maps with user-defined thresholds
✅ Immediate visual feedback when editing color scales
✅ Fewer rendering errors from invalid custom color inputs

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

@codeant-ai-for-open-source
Copy link
Contributor

CodeAnt AI is reviewing your PR.


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@codeant-ai-for-open-source codeant-ai-for-open-source bot added the size:L This PR changes 100-499 lines, ignoring generated files label Dec 18, 2025
@codeant-ai-for-open-source
Copy link
Contributor

Nitpicks 🔍

🔒 No security issues identified
⚡ Recommended areas for review

  • d3 API compatibility
    The new code uses d3.scale.linear() and d3.geo.path(). If the project uses a different major d3 version (v4+), these APIs are deprecated/renamed (d3.scaleLinear, d3.geoPath). Verify d3 version compatibility to avoid runtime errors.

  • Empty color handling
    The sample configuration in the PR description allows an empty string for a color (e.g., "") to denote transparency, but normalizeColorKeyword currently treats empty strings as invalid and falls back to #000000. This will map empty colors to black rather than transparent/none which is likely not intended.

  • DOM-only API use
    normalizeColorKeyword calls new Option() to validate named CSS colors. This relies on a browser DOM being present; tests or server-side execution without a DOM will throw. Consider guarding or using a safer check to avoid runtime errors in non-browser environments.

  • Prop name mismatch
    The component uses customColorScale from props but PropTypes defines customColorRules. This mismatch means prop type validation won't match the real prop and consumers may pass the wrong prop name. Ensure the prop name is consistent in PropTypes and in the function signature.

Comment on lines +63 to +66
// Named CSS colors and system colors
const s = new Option().style;
s.color = c.toLowerCase();
if (s.color) return c;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Runtime error risk in non-browser environments: new Option() is used to detect named CSS colors which can throw or be undefined during server-side rendering or unit tests; guard usage with a typeof check or try/catch so code doesn't crash outside a browser. [possible bug]

Severity Level: Critical 🚨

Suggested change
// Named CSS colors and system colors
const s = new Option().style;
s.color = c.toLowerCase();
if (s.color) return c;
// Named CSS colors and system colors - guard for non-browser environments
try {
if (typeof Option !== 'undefined') {
const s = new Option().style;
s.color = c.toLowerCase();
if (s.color) return c;
}
} catch {
// ignore environment where Option is not available
}
Why it matters? ⭐

new Option() is a DOM API and will throw (Option is not defined) in Node/SSR/test environments.
Wrapping the usage in a typeof check / try-catch as suggested prevents runtime crashes outside browsers — a meaningful, low-risk hardening.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset-frontend/plugins/legacy-plugin-chart-country-map/src/CountryMap.js
**Line:** 63:66
**Comment:**
	*Possible Bug: Runtime error risk in non-browser environments: `new Option()` is used to detect named CSS colors which can throw or be undefined during server-side rendering or unit tests; guard usage with a typeof check or try/catch so code doesn't crash outside a browser.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.


function rgbaToHex(rgba) {
if (typeof rgba === 'string') return rgba;
if (Array.isArray(rgba)) return rgbaToHex(rgba[0]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Incorrect handling of numeric color arrays in rgbaToHex: if rgba is an array of numeric channels (e.g. [r,g,b]) the function recurses into the first numeric element and returns null; explicitly handle numeric arrays of channels to produce a hex string. [type error]

Severity Level: Minor ⚠️

Suggested change
if (Array.isArray(rgba)) return rgbaToHex(rgba[0]);
if (Array.isArray(rgba)) {
// Handle numeric channel arrays like [r, g, b]
if (rgba.length >= 3 && typeof rgba[0] === 'number') {
const [r, g, b] = rgba;
if (r === undefined || g === undefined || b === undefined) return null;
const toHex = n => {
const hex = Math.round(n).toString(16);
return hex.length === 1 ? `0${hex}` : hex;
};
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
}
return rgbaToHex(rgba[0]);
}
Why it matters? ⭐

The current Array handling recurses into the first element, so numeric arrays like [r,g,b] become a number and return null. The proposed explicit handling of numeric channel arrays fixes that bug and returns a proper hex string.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset-frontend/plugins/legacy-plugin-chart-country-map/src/CountryMap.js
**Line:** 80:80
**Comment:**
	*Type Error: Incorrect handling of numeric color arrays in `rgbaToHex`: if `rgba` is an array of numeric channels (e.g. [r,g,b]) the function recurses into the first numeric element and returns null; explicitly handle numeric arrays of channels to produce a hex string.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

@@ -53,22 +108,127 @@ function CountryMap(element, props) {
country,
linearColorScheme,
numberFormat,
customColorScale = [],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Logic bug: the component reads customColorScale from props but propTypes define customColorRules; if the parent passes customColorRules the new code will ignore it and always use an empty array, breaking custom color rules. Use the actual prop or fall back to customColorRules. [logic error]

Severity Level: Minor ⚠️

Suggested change
customColorScale = [],
customColorScale = props.customColorRules || [],
Why it matters? ⭐

The PR added propTypes.customColorRules but the component reads customColorScale from props — that's a real mismatch.
If callers pass customColorRules the current code will ignore them. The suggested fallback to props.customColorRules is valid and fixes the logic bug.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset-frontend/plugins/legacy-plugin-chart-country-map/src/CountryMap.js
**Line:** 111:111
**Comment:**
	*Logic Error: Logic bug: the component reads `customColorScale` from props but propTypes define `customColorRules`; if the parent passes `customColorRules` the new code will ignore it and always use an empty array, breaking custom color rules. Use the actual prop or fall back to `customColorRules`.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

let parsedColorScale = [];

try {
parsedColorScale = customColorScale ? JSON.parse(customColorScale) : [];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: JSON.parse is being called unconditionally on customColorScale which can be a non-string (already-parsed array/object) or a string that parses to non-array (e.g., "null"); this will either throw (caught and silently drop user's config) or produce a non-array value; only parse when the input is a non-empty string and ensure the parsed result is an array, otherwise accept an already-array value or fall back to []. [type error]

Severity Level: Minor ⚠️

Suggested change
parsedColorScale = customColorScale ? JSON.parse(customColorScale) : [];
if (typeof customColorScale === 'string') {
const trimmed = customColorScale.trim();
if (trimmed === '') {
parsedColorScale = [];
} else {
const parsed = JSON.parse(trimmed);
parsedColorScale = Array.isArray(parsed) ? parsed : [];
}
} else if (Array.isArray(customColorScale)) {
parsedColorScale = customColorScale;
} else {
parsedColorScale = [];
}
Why it matters? ⭐

Good catch — the current line blindly calls JSON.parse on anything truthy which can throw or produce non-array results (or previously swallow arrays due to the catch). The proposed guard (only parse strings, accept arrays, ensure Array.isArray) fixes a real bug and makes the prop stable for consumers.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset-frontend/plugins/legacy-plugin-chart-country-map/src/transformProps.js
**Line:** 33:33
**Comment:**
	*Type Error: JSON.parse is being called unconditionally on `customColorScale` which can be a non-string (already-parsed array/object) or a string that parses to non-array (e.g., "null"); this will either throw (caught and silently drop user's config) or produce a non-array value; only parse when the input is a non-empty string and ensure the parsed result is an array, otherwise accept an already-array value or fall back to [].

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

@@ -35,5 +47,6 @@ export default function transformProps(chartProps) {
numberFormat,
colorScheme,
sliceId,
customColorScale: parsedColorScale,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The returned customColorScale may be null or a non-array value if parsing returned such a value; ensure the value exported in props is always an array so downstream consumers that expect an array won't crash. [possible bug]

Severity Level: Critical 🚨

Suggested change
customColorScale: parsedColorScale,
customColorScale: Array.isArray(parsedColorScale) ? parsedColorScale : [],
Why it matters? ⭐

Ensuring the returned prop is always an array is sensible defensive programming. If the parse step is missed or returns null/object, downstream code expecting an array would break; wrapping with Array.isArray(...) is a small, safe hardening.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset-frontend/plugins/legacy-plugin-chart-country-map/src/transformProps.js
**Line:** 50:50
**Comment:**
	*Possible Bug: The returned `customColorScale` may be null or a non-array value if parsing returned such a value; ensure the value exported in props is always an array so downstream consumers that expect an array won't crash.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

@codeant-ai-for-open-source
Copy link
Contributor

CodeAnt AI finished reviewing your PR.

Copy link
Contributor

@bito-code-review bito-code-review bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Agent Run #d66de9

Actionable Suggestions - 2
  • superset-frontend/plugins/legacy-plugin-chart-country-map/src/CountryMap.js - 2
Additional Suggestions - 3
  • superset-frontend/plugins/legacy-plugin-chart-country-map/src/transformProps.js - 1
    • Convert JS to TS for type safety · Line 27-27
      This JavaScript file should be converted to TypeScript to align with codebase standards that prohibit new JavaScript files and require type safety. The added customColorScale parsing logic is correct but would benefit from TypeScript types for better maintainability.
  • superset-frontend/plugins/legacy-plugin-chart-country-map/src/CountryMap.js - 2
    • Outdated CSS color regexes · Line 57-60
      The regexes for CSS color functions only match comma-separated syntax (e.g., rgb(255, 0, 0)), but modern CSS supports space-separated syntax (e.g., rgb(255 0 0)). This could cause valid modern color strings to be rejected as invalid, falling back to '#000000'.
    • Unused function (dead code) · Line 78-89
      The rgbaToHex function is defined but never called in the codebase. While it might be intended for future color handling, removing it now keeps the code clean and avoids maintenance overhead.
Review Details
  • Files reviewed - 3 · Commit Range: ca2e1b8..30fcc46
    • superset-frontend/plugins/legacy-plugin-chart-country-map/src/CountryMap.js
    • superset-frontend/plugins/legacy-plugin-chart-country-map/src/controlPanel.ts
    • superset-frontend/plugins/legacy-plugin-chart-country-map/src/transformProps.js
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Eslint (Linter) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Default Agent You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@@ -41,10 +41,65 @@ const propTypes = {
linearColorScheme: PropTypes.string,
mapBaseUrl: PropTypes.string,
numberFormat: PropTypes.string,
customColorRules: PropTypes.array,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prop name mismatch in PropTypes

The PropTypes definition adds 'customColorRules' as an array prop, but the component destructures 'customColorScale' from props. This mismatch could prevent the prop from being properly validated or used, potentially leading to incorrect color scaling behavior if the prop is passed.

Code Review Run #d66de9


Should Bito avoid suggestions like this for future reviews? (Manage Rules)

  • Yes, avoid them

Comment on lines +159 to +169
percentColorScale = d3.scale
.linear()
.domain(domainPerc)
.range(rangeColors)
.clamp(true)
// Remove interpolation to avoid blending between steps - always return lower boundary
.interpolate(function (a, b) {
return function () {
return a;
};
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incorrect Color Scale Implementation

The percentColorScale uses d3.scale.linear with a custom interpolate function that always returns the lower boundary color, which prevents the last color in the range from ever being used. For step-based color scales with explicit thresholds, d3.scale.threshold should be used instead, as it properly maps intervals to discrete colors without this limitation.

Code Review Run #d66de9


Should Bito avoid suggestions like this for future reviews? (Manage Rules)

  • Yes, avoid them

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

plugins size/L size:L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant