Compare commits

...

4 Commits

Author SHA1 Message Date
Evan Rusackas
acf3635500 fix(test): Add missing jest-dom import to fix CountryMap tests
The tests were failing because jest-dom matchers (toHaveClass, toHaveAttribute,
toHaveStyle) were being used without importing @testing-library/jest-dom.
Also added CSS import to ensure styles are available during testing.
2025-08-03 00:02:37 -07:00
Evan Rusackas
1926212f0e removing extraneous stuff. 2025-08-02 23:21:16 -07:00
Evan Rusackas
86d5d4b4a8 fix(country-map): improve tooltip readability with proper styling
Fixes #28458

The Country Map tooltip was not readable as it displayed information in fixed text elements
instead of a proper tooltip that follows the mouse cursor like the World Map does.

Changes:
- Added tooltip CSS with dark background and white text for better contrast
- Implemented mouse-following tooltip using D3 event coordinates
- Created tooltip HTML structure with title and value sections
- Added transition effects for smooth show/hide
- Added tests to verify tooltip DOM elements are created

The tooltip now behaves consistently with other map visualizations in Superset.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-02 23:14:19 -07:00
Evan Rusackas
2029fd0131 fix(table): hide conditional formatting color options without time comparison
Fixes #34141

The "Green for increase, red for decrease" and "Red for increase, green for decrease"
color scheme options were showing in table chart conditional formatting even when no
time comparison was active. These options only work with time comparison data, so they
should be hidden when time_compare is empty.

Changes:
- Modified both table chart control panels to dynamically show/hide color options based on time comparison
- extraColorChoices now depends on hasTimeComparison check
- Applied fix to both regular table chart and AG Grid table chart for consistency

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-02 23:01:00 -07:00
5 changed files with 190 additions and 20 deletions

View File

@@ -59,3 +59,29 @@
cursor: pointer;
stroke: #eee;
}
.superset-legacy-chart-country-map .tooltip {
position: absolute;
text-align: left;
padding: 10px;
font-size: 12px;
background: rgba(0, 0, 0, 0.8);
color: white;
border-radius: 4px;
pointer-events: none;
opacity: 0;
transition: opacity 0.2s;
}
.superset-legacy-chart-country-map .tooltip.show {
opacity: 1;
}
.superset-legacy-chart-country-map .tooltip .tooltip-title {
font-weight: 600;
margin-bottom: 4px;
}
.superset-legacy-chart-country-map .tooltip .tooltip-value {
font-weight: 300;
}

View File

@@ -100,6 +100,12 @@ function CountryMap(element, props) {
.classed('result-text', true)
.attr('dy', '1em');
// Create tooltip
const tooltip = div
.append('div')
.attr('class', 'tooltip')
.style('opacity', 0);
let centered;
const clicked = function clicked(d) {
@@ -181,12 +187,38 @@ function CountryMap(element, props) {
region => region.country_id === d.properties.ISO,
);
updateMetrics(result);
// Show tooltip
let name = '';
if (d && d.properties) {
if (d.properties.ID_2) {
name = d.properties.NAME_2;
} else {
name = d.properties.NAME_1;
}
}
const value = result.length > 0 ? format(result[0].metric) : 'No data';
tooltip
.classed('show', true)
.html(
`<div class="tooltip-title">${name}</div>` +
`<div class="tooltip-value">${value}</div>`,
);
};
const mousemove = function mousemove() {
tooltip
.style('left', `${d3.event.pageX + 15}px`)
.style('top', `${d3.event.pageY - 28}px`);
};
const mouseout = function mouseout() {
d3.select(this).style('fill', colorFn);
bigText.text('');
resultText.text('');
tooltip.classed('show', false);
};
function drawMap(mapData) {
@@ -225,6 +257,7 @@ function CountryMap(element, props) {
.attr('vector-effect', 'non-scaling-stroke')
.style('fill', colorFn)
.on('mouseenter', mouseenter)
.on('mousemove', mousemove)
.on('mouseout', mouseout)
.on('click', clicked);
}

View File

@@ -0,0 +1,95 @@
/**
* 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 CountryMap from '../src/CountryMap';
import '../src/CountryMap.css';
describe('CountryMap', () => {
let container;
let mockProps;
beforeEach(() => {
container = document.createElement('div');
container.style.width = '800px';
container.style.height = '600px';
document.body.appendChild(container);
mockProps = {
data: [
{ country_id: 'USA', metric: 100 },
{ country_id: 'CAN', metric: 50 },
],
width: 800,
height: 600,
country: 'usa',
linearColorScheme: 'greenBlue',
numberFormat: '.3s',
colorScheme: null,
sliceId: 1,
};
});
afterEach(() => {
document.body.removeChild(container);
});
it('should create the necessary DOM elements', () => {
CountryMap(container, mockProps);
// Check if main container has the correct class
expect(container).toHaveClass('superset-legacy-chart-country-map');
// Check if SVG is created
const svg = container.querySelector('svg');
expect(svg).toBeTruthy();
expect(svg).toHaveAttribute('width', '800');
expect(svg).toHaveAttribute('height', '600');
// Check if tooltip div is created
const tooltip = container.querySelector('.tooltip');
expect(tooltip).toBeTruthy();
expect(tooltip).toHaveStyle({ opacity: '0' });
});
it('should create map layers', () => {
CountryMap(container, mockProps);
// Check if map layer exists
const mapLayer = container.querySelector('.map-layer');
expect(mapLayer).toBeTruthy();
// Check if text layer exists
const textLayer = container.querySelector('.text-layer');
expect(textLayer).toBeTruthy();
});
it('should apply tooltip styles', () => {
CountryMap(container, mockProps);
const tooltip = container.querySelector('.tooltip');
expect(tooltip).toBeTruthy();
// Check if tooltip has the correct class
expect(tooltip).toHaveClass('tooltip');
// Check if tooltip has opacity 0 initially
expect(tooltip).toHaveStyle({ opacity: '0' });
});
});

View File

@@ -673,16 +673,6 @@ const config: ControlPanelConfig = {
type: 'ConditionalFormattingControl',
renderTrigger: true,
label: t('Custom conditional formatting'),
extraColorChoices: [
{
value: ColorSchemeEnum.Green,
label: t('Green for increase, red for decrease'),
},
{
value: ColorSchemeEnum.Red,
label: t('Red for increase, green for decrease'),
},
],
description: t(
'Apply conditional color formatting to numeric columns',
),
@@ -695,6 +685,23 @@ const config: ControlPanelConfig = {
)
? (explore?.datasource as Dataset)?.verbose_map
: (explore?.datasource?.columns ?? {});
// Only show increase/decrease color options when time comparison is active
const hasTimeComparison = !isEmpty(
explore?.form_data?.time_compare,
);
const extraColorChoices = hasTimeComparison
? [
{
value: ColorSchemeEnum.Green,
label: t('Green for increase, red for decrease'),
},
{
value: ColorSchemeEnum.Red,
label: t('Red for increase, green for decrease'),
},
]
: [];
const chartStatus = chart?.chartStatus;
const { colnames, coltypes } =
chart?.queriesResponse?.[0] ?? {};
@@ -725,6 +732,7 @@ const config: ControlPanelConfig = {
removeIrrelevantConditions: chartStatus === 'success',
columnOptions,
verboseMap,
extraColorChoices,
};
},
},

View File

@@ -730,16 +730,6 @@ const config: ControlPanelConfig = {
type: 'ConditionalFormattingControl',
renderTrigger: true,
label: t('Custom conditional formatting'),
extraColorChoices: [
{
value: ColorSchemeEnum.Green,
label: t('Green for increase, red for decrease'),
},
{
value: ColorSchemeEnum.Red,
label: t('Red for increase, green for decrease'),
},
],
description: t(
'Apply conditional color formatting to numeric columns',
),
@@ -752,6 +742,23 @@ const config: ControlPanelConfig = {
)
? (explore?.datasource as Dataset)?.verbose_map
: (explore?.datasource?.columns ?? {});
// Only show increase/decrease color options when time comparison is active
const hasTimeComparison = !isEmpty(
explore?.form_data?.time_compare,
);
const extraColorChoices = hasTimeComparison
? [
{
value: ColorSchemeEnum.Green,
label: t('Green for increase, red for decrease'),
},
{
value: ColorSchemeEnum.Red,
label: t('Red for increase, green for decrease'),
},
]
: [];
const chartStatus = chart?.chartStatus;
const { colnames, coltypes } =
chart?.queriesResponse?.[0] ?? {};
@@ -782,6 +789,7 @@ const config: ControlPanelConfig = {
removeIrrelevantConditions: chartStatus === 'success',
columnOptions,
verboseMap,
extraColorChoices,
};
},
},