codeant-ai-for-open-source[bot] commented on code in PR #36323:
URL: https://github.com/apache/superset/pull/36323#discussion_r2585818139
##########
superset-frontend/plugins/legacy-plugin-chart-country-map/src/CountryMap.js:
##########
@@ -128,45 +120,18 @@ function CountryMap(element, props) {
'transform',
`translate(${halfWidth},${halfHeight})scale(${k})translate(${-x},${-y})`,
);
- textLayer
- .style('opacity', 0)
- .attr(
- 'transform',
- `translate(0,0)translate(${x},${hasCenter ? y - 5 : 45})`,
- )
- .transition()
- .duration(750)
- .style('opacity', 1);
- bigText
- .transition()
- .duration(750)
- .style('font-size', hasCenter ? 6 : 16);
- resultText
- .transition()
- .duration(750)
- .style('font-size', hasCenter ? 16 : 24);
};
backgroundRect.on('click', clicked);
- const selectAndDisplayNameOfRegion = function selectAndDisplayNameOfRegion(
- feature,
- ) {
- let name = '';
+ const getNameOfRegion = function getNameOfRegion(feature) {
if (feature && feature.properties) {
if (feature.properties.ID_2) {
Review Comment:
**Suggestion:** Logic bug: `getNameOfRegion` checks
`feature.properties.ID_2` with a truthy test, which treats zero or other falsy
but valid IDs as "missing" and incorrectly falls back to NAME_1; change the
check to test for presence (null/undefined) instead of truthiness. [logic error]
**Severity Level:** Minor ⚠️
```suggestion
// Use a null/undefined check so valid falsy IDs (e.g. 0) are handled
correctly
if (feature.properties.ID_2 != null) {
```
<details>
<summary><b>Why it matters? ⭐ </b></summary>
The current truthy test incorrectly treats valid falsy values (e.g. 0) as
absent. Changing to a null/undefined check (!= null) is correct and fixes a
logic bug visible in this hunk.
</details>
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:**
superset-frontend/plugins/legacy-plugin-chart-country-map/src/CountryMap.js
**Line:** 129:129
**Comment:**
*Logic Error: Logic bug: `getNameOfRegion` checks
`feature.properties.ID_2` with a truthy test, which treats zero or other falsy
but valid IDs as "missing" and incorrectly falls back to NAME_1; change the
check to test for presence (null/undefined) instead of truthiness.
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.
```
</details>
##########
superset-frontend/plugins/legacy-plugin-chart-country-map/test/CountryMap.test.tsx:
##########
@@ -0,0 +1,130 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import '@testing-library/jest-dom';
+import { render, fireEvent } from '@testing-library/react';
Review Comment:
**Suggestion:** The test uses `waitFor` but it is not imported; add
`waitFor` to the testing-library import so the fix that waits for async DOM
updates can be used safely. [race condition]
**Severity Level:** Minor ⚠️
```suggestion
import { render, fireEvent, waitFor } from '@testing-library/react';
```
<details>
<summary><b>Why it matters? ⭐ </b></summary>
Adding `waitFor` is reasonable and necessary if you apply the other
suggestions that replace synchronous DOM queries with `waitFor`. On its own
it'll introduce an unused import until the tests are updated, but in the
context of this PR it's a required, harmless change to enable reliable async
assertions.
</details>
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:**
superset-frontend/plugins/legacy-plugin-chart-country-map/test/CountryMap.test.tsx
**Line:** 21:21
**Comment:**
*Race Condition: The test uses `waitFor` but it is not imported; add
`waitFor` to the testing-library import so the fix that waits for async DOM
updates can be used safely.
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.
```
</details>
##########
superset-frontend/plugins/legacy-plugin-chart-country-map/test/CountryMap.test.tsx:
##########
@@ -0,0 +1,130 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import '@testing-library/jest-dom';
+import { render, fireEvent } from '@testing-library/react';
+import d3 from 'd3';
+import ReactCountryMap from '../src/ReactCountryMap';
+
+jest.spyOn(d3, 'json');
+
+type Projection = ((...args: unknown[]) => void) & {
+ scale: () => Projection;
+ center: () => Projection;
+ translate: () => Projection;
+};
+
+type PathFn = (() => string) & {
+ projection: jest.Mock;
+ bounds: jest.Mock<[[number, number], [number, number]]>;
+ centroid: jest.Mock<[number, number]>;
+};
+
+const mockPath: PathFn = jest.fn(() => 'M10 10 L20 20') as unknown as PathFn;
+mockPath.projection = jest.fn();
+mockPath.bounds = jest.fn(() => [
+ [0, 0],
+ [100, 100],
+]);
+mockPath.centroid = jest.fn(() => [50, 50]);
+
+jest.spyOn(d3.geo, 'path').mockImplementation(() => mockPath);
+
+// Mock d3.geo.mercator
+jest.spyOn(d3.geo, 'mercator').mockImplementation(() => {
+ const proj = (() => {}) as Projection;
+ proj.scale = () => proj;
+ proj.center = () => proj;
+ proj.translate = () => proj;
+ return proj;
+});
+
+// Mock d3.mouse
+jest.spyOn(d3, 'mouse').mockReturnValue([100, 50]);
+
+const mockMapData = {
+ type: 'FeatureCollection',
+ features: [
+ {
+ type: 'Feature',
+ properties: { ISO: 'CAN', NAME_1: 'Canada' },
+ geometry: {},
+ },
+ ],
+};
+
+type D3JsonCallback = (error: Error | null, data: unknown) => void;
+
+describe('CountryMap (legacy d3)', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('renders a map after d3.json loads data', async () => {
+ d3.json.mockImplementation((_url: string, cb: D3JsonCallback) =>
+ cb(null, mockMapData),
+ );
+
+ render(
+ <ReactCountryMap
+ width={500}
+ height={300}
+ data={[{ country_id: 'CAN', metric: 100 }]}
+ country="canada"
+ linearColorScheme="bnbColors"
+ colorScheme=""
+ numberFormat=".2f"
+ />,
+ );
+
+ expect(d3.json).toHaveBeenCalledTimes(1);
+
+ const region = await document.querySelector('path.region');
+ expect(region).not.toBeNull();
+ });
+
+ it('shows tooltip on mouseenter/mousemove/mouseout', async () => {
+ d3.json.mockImplementation((_url: string, cb: D3JsonCallback) =>
+ cb(null, mockMapData),
+ );
+
+ render(
+ <ReactCountryMap
+ width={500}
+ height={300}
+ data={[{ country_id: 'CAN', metric: 100 }]}
+ country="canada"
+ linearColorScheme="bnbColors"
+ colorScheme=""
+ />,
+ );
+
+ const region = await document.querySelector('path.region');
+ expect(region).not.toBeNull();
+
+ const popup = document.querySelector('.hover-popup');
Review Comment:
**Suggestion:** The second test also queries the DOM immediately after
render for both the region and the tooltip; `await document.querySelector` does
not wait for async updates and can make the test flaky—use `waitFor` for both
queries to reliably wait for the elements to be mounted. [race condition]
**Severity Level:** Minor ⚠️
```suggestion
const region = await waitFor(() =>
document.querySelector('path.region'));
expect(region).not.toBeNull();
const popup = await waitFor(() =>
document.querySelector('.hover-popup'));
```
<details>
<summary><b>Why it matters? ⭐ </b></summary>
Both queries are subject to the same async rendering timing and the file
currently misuses `await` on a sync query. Converting both to `waitFor` will
reduce flakiness and correctly wait for the elements to be mounted.
</details>
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:**
superset-frontend/plugins/legacy-plugin-chart-country-map/test/CountryMap.test.tsx
**Line:** 118:121
**Comment:**
*Race Condition: The second test also queries the DOM immediately after
render for both the region and the tooltip; `await document.querySelector` does
not wait for async updates and can make the test flaky—use `waitFor` for both
queries to reliably wait for the elements to be mounted.
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.
```
</details>
##########
superset-frontend/plugins/legacy-plugin-chart-country-map/test/CountryMap.test.tsx:
##########
@@ -0,0 +1,130 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import '@testing-library/jest-dom';
+import { render, fireEvent } from '@testing-library/react';
+import d3 from 'd3';
+import ReactCountryMap from '../src/ReactCountryMap';
+
+jest.spyOn(d3, 'json');
+
+type Projection = ((...args: unknown[]) => void) & {
+ scale: () => Projection;
+ center: () => Projection;
+ translate: () => Projection;
+};
+
+type PathFn = (() => string) & {
+ projection: jest.Mock;
+ bounds: jest.Mock<[[number, number], [number, number]]>;
+ centroid: jest.Mock<[number, number]>;
+};
+
+const mockPath: PathFn = jest.fn(() => 'M10 10 L20 20') as unknown as PathFn;
+mockPath.projection = jest.fn();
+mockPath.bounds = jest.fn(() => [
+ [0, 0],
+ [100, 100],
+]);
+mockPath.centroid = jest.fn(() => [50, 50]);
+
+jest.spyOn(d3.geo, 'path').mockImplementation(() => mockPath);
+
+// Mock d3.geo.mercator
+jest.spyOn(d3.geo, 'mercator').mockImplementation(() => {
+ const proj = (() => {}) as Projection;
+ proj.scale = () => proj;
+ proj.center = () => proj;
+ proj.translate = () => proj;
+ return proj;
+});
+
+// Mock d3.mouse
+jest.spyOn(d3, 'mouse').mockReturnValue([100, 50]);
+
+const mockMapData = {
+ type: 'FeatureCollection',
+ features: [
+ {
+ type: 'Feature',
+ properties: { ISO: 'CAN', NAME_1: 'Canada' },
+ geometry: {},
+ },
+ ],
+};
+
+type D3JsonCallback = (error: Error | null, data: unknown) => void;
+
+describe('CountryMap (legacy d3)', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('renders a map after d3.json loads data', async () => {
+ d3.json.mockImplementation((_url: string, cb: D3JsonCallback) =>
+ cb(null, mockMapData),
+ );
+
+ render(
+ <ReactCountryMap
+ width={500}
+ height={300}
+ data={[{ country_id: 'CAN', metric: 100 }]}
+ country="canada"
+ linearColorScheme="bnbColors"
+ colorScheme=""
+ numberFormat=".2f"
+ />,
+ );
+
+ expect(d3.json).toHaveBeenCalledTimes(1);
+
+ const region = await document.querySelector('path.region');
Review Comment:
**Suggestion:** The test queries the DOM immediately after rendering using
`document.querySelector` with `await` (which does not wait). This creates a
race condition where the component's async state updates (triggered by the
mocked d3.json callback) may not have applied yet; replace the direct query
with `await waitFor(() => document.querySelector(...))` to wait for the element
to appear. [race condition]
**Severity Level:** Minor ⚠️
```suggestion
const region = await waitFor(() =>
document.querySelector('path.region'));
```
<details>
<summary><b>Why it matters? ⭐ </b></summary>
The existing code incorrectly uses `await` on a synchronous DOM query, which
doesn't wait for async rendering triggered by the mocked d3.json. Replacing it
with `await waitFor(...)` fixes a real race condition and makes the test
deterministic.
</details>
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:**
superset-frontend/plugins/legacy-plugin-chart-country-map/test/CountryMap.test.tsx
**Line:** 98:98
**Comment:**
*Race Condition: The test queries the DOM immediately after rendering
using `document.querySelector` with `await` (which does not wait). This creates
a race condition where the component's async state updates (triggered by the
mocked d3.json callback) may not have applied yet; replace the direct query
with `await waitFor(() => document.querySelector(...))` to wait for the element
to appear.
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.
```
</details>
##########
superset-frontend/plugins/legacy-plugin-chart-country-map/src/CountryMap.js:
##########
@@ -176,17 +141,34 @@ function CountryMap(element, props) {
c = d3.rgb(c).darker().toString();
}
d3.select(this).style('fill', c);
- selectAndDisplayNameOfRegion(d);
+ // Display information popup
const result = data.filter(
region => region.country_id === d.properties.ISO,
);
- updateMetrics(result);
+
+ const position = d3.mouse(svg.node());
Review Comment:
**Suggestion:** Positioning bug: the hover popup position is computed using
`d3.mouse(svg.node())`, which gives coordinates relative to the SVG; the popup
is appended inside the container div, so using SVG coordinates can mis-position
the HTML popup if the SVG is offset inside the container — use the container's
DOM node (div.node()) to compute coordinates relative to the container instead.
[possible bug]
**Severity Level:** Critical 🚨
```suggestion
const position = d3.mouse(div.node());
```
<details>
<summary><b>Why it matters? ⭐ </b></summary>
The popup is appended to the container div; computing mouse coordinates
relative to the SVG can misalign the HTML popup if the SVG isn't at container
origin. Using div.node() is more robust for positioning the appended DOM node.
</details>
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:**
superset-frontend/plugins/legacy-plugin-chart-country-map/src/CountryMap.js
**Line:** 149:149
**Comment:**
*Possible Bug: Positioning bug: the hover popup position is computed
using `d3.mouse(svg.node())`, which gives coordinates relative to the SVG; the
popup is appended inside the container div, so using SVG coordinates can
mis-position the HTML popup if the SVG is offset inside the container — use the
container's DOM node (div.node()) to compute coordinates relative to the
container instead.
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.
```
</details>
##########
superset-frontend/plugins/legacy-plugin-chart-country-map/src/ReactCountryMap.jsx:
##########
@@ -43,33 +43,29 @@ export default styled(CountryMap)`
pointer-events: all;
}
+ .superset-legacy-chart-country-map .hover-popup {
+ position: absolute;
+ color: ${theme.colorTextSecondary};
+ display: none;
+ padding: 4px;
+ border-radius: 1px;
+ background-color: ${theme.colorBgElevated};
+ box-shadow: ${theme.boxShadow};
Review Comment:
**Suggestion:** `theme.boxShadow` may be undefined for some theme configs
and would render the literal `undefined` in CSS; provide a safe fallback (e.g.,
`none`) to avoid invalid CSS being emitted. [possible bug]
**Severity Level:** Critical 🚨
```suggestion
box-shadow: ${theme.boxShadow || 'none'};
```
<details>
<summary><b>Why it matters? ⭐ </b></summary>
Providing a fallback for `theme.boxShadow` avoids emitting `box-shadow:
undefined;` if a theme lacks that property.
It's a small, safe hardening that prevents invalid CSS and is easy to apply.
The change is not strictly required if the theme guarantees the value, but it's
a sensible defensive fix.
</details>
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:**
superset-frontend/plugins/legacy-plugin-chart-country-map/src/ReactCountryMap.jsx
**Line:** 53:53
**Comment:**
*Possible Bug: `theme.boxShadow` may be undefined for some theme
configs and would render the literal `undefined` in CSS; provide a safe
fallback (e.g., `none`) to avoid invalid CSS being emitted.
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.
```
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]