Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 7 additions & 10 deletions src/client/map/MapSet/MapTooltip/getMapTooltip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import './getMapTooltip.css';
* - Uses layer configuration to determine tooltip attributes and formatting.
* - Tooltip can be customized via geojsonOptions in datasource configuration with tooltipSettings:
* - attributes: array of attribute definitions (key, label, unit, decimalPlaces).
* - labelTemplate: optional template applied to all labels (supports [key] substitution).
* - excludeKeys: optional array of attribute keys to exclude from the tooltip.
* - nativeStyles: custom CSS styles for tooltip container.
* - nativeClassName: additional CSS class names for tooltip container.
* - title: optional tooltip title.
Expand Down Expand Up @@ -57,7 +59,10 @@ export const getMapTooltip = ({

// Use configured attributes if available
if (tooltipSettings?.attributes && Array.isArray(tooltipSettings.attributes)) {
tooltipProperties = getTooltipAttributes(tooltipSettings.attributes, featureProperties);
tooltipProperties = getTooltipAttributes(tooltipSettings.attributes, featureProperties, {
labelTemplate: tooltipSettings?.labelTemplate,
excludeKeys: tooltipSettings?.excludeKeys,
});
}
// If no valid tooltip properties, do not show tooltip
if (!tooltipProperties || tooltipProperties.length === 0) {
Expand All @@ -72,16 +77,8 @@ export const getMapTooltip = ({
.map(({ key, label, value, unit }) => {
const valueStr = value == null ? '' : String(value);

// Replace all [key] patterns in the label with the corresponding featureProperties value
let displayLabel = label;
if (typeof label === 'string') {
displayLabel = label.replace(/\[([^\]]+)\]/g, (_, k) =>
featureProperties[k] != null ? featureProperties[k] : `[${k}]`
);
}

return `<div class="ptr-NativeMapTooltip-row" key="${key}">
<span class="ptr-NativeMapTooltip-label">${displayLabel + (valueStr ? ':' : '')}</span>
<span class="ptr-NativeMapTooltip-label">${label + (valueStr ? ':' : '')}</span>
<span class="ptr-NativeMapTooltip-value">
${valueStr}${unit ? ` ${unit}` : ''}
</span>
Expand Down
9 changes: 6 additions & 3 deletions src/client/map/MapSet/handleMapHover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ interface HandleMapHoverParams {
* Handles hover events on the map and sets tooltip if enabled.
*
* - Shows tooltip with feature properties if enabled in layer configuration.
* - Tooltip attributes and formatting are defined in geojsonOptions.tooltipSettings.
* - Tooltip attributes and formatting are defined in geojsonOptions.tooltipSettings (attributes, labelTemplate, excludeKeys).
* - If no attributes are defined, uses 'value' property if present.
* - Tooltip is hidden if not enabled or no valid properties found.
* - Also updates layer hover state for cursor feedback.
Expand Down Expand Up @@ -63,7 +63,7 @@ export function handleMapHover({
? mapLayers.find((layer: RenderingLayer) => layer.key === layerId)
: undefined;

const featureProperties = event.object?.properties;
const featureProperties = event.object?.properties || {};

const config = parseDatasourceConfiguration(mapLayer?.datasource?.configuration);

Expand All @@ -89,7 +89,10 @@ export function handleMapHover({
* Otherwise, tooltip will not be shown.
*/
if (tooltipSettings?.attributes && Array.isArray(tooltipSettings.attributes)) {
tooltipProperties = getTooltipAttributes(tooltipSettings.attributes, featureProperties);
tooltipProperties = getTooltipAttributes(tooltipSettings.attributes, featureProperties, {
labelTemplate: tooltipSettings?.labelTemplate,
excludeKeys: tooltipSettings?.excludeKeys,
});
}

/**
Expand Down
32 changes: 29 additions & 3 deletions src/client/story/utils/getTooltipAttributes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,54 @@ export interface TooltipAttribute {
decimalPlaces?: number;
}

/**
* Optional tooltip processing settings.
* @property {string} [labelTemplate] - Template applied to all labels (supports [key] substitution).
* @property {string[]} [excludeKeys] - Attribute keys to exclude from tooltip output.
*/
export interface TooltipAttributeOptions {
labelTemplate?: string;
excludeKeys?: string[];
}

/**
* Maps feature properties to tooltip attributes based on settings.
* Rounds numbers if decimalPlaces is specified.
* Supports label substitution for [key] placeholders.
*
* @param {TooltipAttribute[]} attributes - Array of attribute settings.
* @param {Record<string, any>} featureProperties - Properties of the hovered feature.
* @param {TooltipAttributeOptions} [options] - Optional label/exclusion settings.
* @returns {TooltipAttribute[]} Array of tooltip attributes to display.
*/
export function getTooltipAttributes(
attributes: TooltipAttribute[],
featureProperties: Record<string, any>
featureProperties: Record<string, any>,
options?: TooltipAttributeOptions
): TooltipAttribute[] {
const properties = featureProperties ?? {};
const excludeKeys = new Set(options?.excludeKeys ?? []);
const labelTemplate = options?.labelTemplate;

return attributes
.filter((attribute) => !excludeKeys.has(attribute.key))
.map((attribute: TooltipAttribute) => {
let value = featureProperties[attribute.key];
let value = properties[attribute.key];
// Round value if decimalPlaces is specified and value is a number
if (typeof value === 'number' && typeof attribute.decimalPlaces === 'number') {
value = Number(value.toFixed(attribute.decimalPlaces));
}

let label = labelTemplate ?? attribute.label ?? '';
if (typeof label === 'string') {
label = label.replace(/\[([^\]]+)\]/g, (_, key) =>
properties[key] != null ? String(properties[key]) : `[${key}]`
);
}

return {
key: attribute.key,
label: attribute.label ?? '',
label,
value,
unit: attribute.unit ?? '',
};
Expand Down