@gooddata/sdk-ui-vis-commons 11.41.0-alpha.0 → 11.41.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { type IResolvedReferenceValues } from "./types.js";
1
+ import { type IResolvedReferenceValues, type ITooltipLocalizedStrings } from "./types.js";
2
2
  /**
3
3
  * Merge order matters: in-chart values override external ones, because the
4
4
  * in-chart value is what the user already sees on the rendered point/feature.
@@ -7,4 +7,4 @@ import { type IResolvedReferenceValues } from "./types.js";
7
7
  *
8
8
  * @internal
9
9
  */
10
- export declare function composeCustomTooltipSectionHtml(content: string, inChartValues: IResolvedReferenceValues, externalValues: IResolvedReferenceValues, fallbackText: string): string;
10
+ export declare function composeCustomTooltipSectionHtml(content: string, inChartValues: IResolvedReferenceValues, externalValues: IResolvedReferenceValues, localizedStrings: ITooltipLocalizedStrings): string;
@@ -9,8 +9,8 @@ import { resolveReferences } from "./referenceResolver.js";
9
9
  *
10
10
  * @internal
11
11
  */
12
- export function composeCustomTooltipSectionHtml(content, inChartValues, externalValues, fallbackText) {
12
+ export function composeCustomTooltipSectionHtml(content, inChartValues, externalValues, localizedStrings) {
13
13
  const merged = { ...externalValues, ...inChartValues };
14
- const resolved = resolveReferences(content, merged, fallbackText);
14
+ const resolved = resolveReferences(content, merged, localizedStrings);
15
15
  return `<div class="gd-viz-tooltip-custom-section">${markdownToHtml(resolved)}</div>`;
16
16
  }
@@ -0,0 +1,11 @@
1
+ import { type IntlShape } from "react-intl";
2
+ import { type ITooltipLocalizedStrings } from "./types.js";
3
+ /**
4
+ * Builds the localized placeholder strings for the non-value reference states,
5
+ * shared by every tooltip consumer (Highcharts, geo) so the wording and the
6
+ * message ids live in one place. `intl` is optional — the Highcharts tooltip
7
+ * formatter receives it optionally — and falls back to English.
8
+ *
9
+ * @internal
10
+ */
11
+ export declare function buildTooltipLocalizedStrings(intl?: IntlShape): ITooltipLocalizedStrings;
@@ -0,0 +1,22 @@
1
+ // (C) 2026 GoodData Corporation
2
+ import { defineMessages } from "react-intl";
3
+ const messages = defineMessages({
4
+ noFetch: { id: "richText.no_fetch" },
5
+ noData: { id: "richText.no_data" },
6
+ multipleItems: { id: "richText.multiple_data" },
7
+ });
8
+ /**
9
+ * Builds the localized placeholder strings for the non-value reference states,
10
+ * shared by every tooltip consumer (Highcharts, geo) so the wording and the
11
+ * message ids live in one place. `intl` is optional — the Highcharts tooltip
12
+ * formatter receives it optionally — and falls back to English.
13
+ *
14
+ * @internal
15
+ */
16
+ export function buildTooltipLocalizedStrings(intl) {
17
+ return {
18
+ noFetch: intl ? `(${intl.formatMessage(messages.noFetch)})` : "(Data could not be retrieved)",
19
+ noData: intl ? `(${intl.formatMessage(messages.noData)})` : "(No data)",
20
+ multipleItems: intl ? `(${intl.formatMessage(messages.multipleItems)})` : "(Multiple items)",
21
+ };
22
+ }
@@ -1,4 +1,4 @@
1
- import { type IResolvedReferenceValues } from "./types.js";
1
+ import { type IResolvedReferenceValues, type ITooltipLocalizedStrings } from "./types.js";
2
2
  /**
3
3
  * Substitutes `{metric/id}` and `{label/id}` references in markdown content
4
4
  * with resolved values from the lookup table.
@@ -9,11 +9,11 @@ import { type IResolvedReferenceValues } from "./types.js";
9
9
  * unintended formatting. `markdownToHtml` understands the backslash escapes.
10
10
  *
11
11
  * @param content - Markdown content with reference placeholders
12
- * @param values - Lookup of `metric/id` and `label/id` keys to formatted values.
12
+ * @param values - Lookup of `metric/id` and `label/id` keys to resolved statuses.
13
13
  * Keys must use a lowercase prefix; LDM identifiers are case-sensitive.
14
- * @param fallbackText - Localized text shown when a reference is recognized
15
- * but no value is available (unknown identifier, point with no value, etc.).
14
+ * @param strings - Localized placeholders for the non-value states (no data,
15
+ * multiple items, could-not-retrieve).
16
16
  *
17
17
  * @internal
18
18
  */
19
- export declare function resolveReferences(content: string, values: IResolvedReferenceValues, fallbackText: string): string;
19
+ export declare function resolveReferences(content: string, values: IResolvedReferenceValues, strings: ITooltipLocalizedStrings): string;
@@ -1,5 +1,6 @@
1
1
  // (C) 2026 GoodData Corporation
2
2
  import { REFERENCE_REGEX_MATCH } from "@gooddata/sdk-ui-kit";
3
+ import { labelKey, metricKey, } from "./types.js";
3
4
  // Markdown metacharacters that, if present in a substituted value, would be
4
5
  // reinterpreted as formatting by `markdownToHtml`. Backslash-escape them so the
5
6
  // parser treats them as literal text. The set covers every char that can start
@@ -9,6 +10,24 @@ const MARKDOWN_METACHARS = /[\\*_`\[\]()!#~+\-]/g;
9
10
  function escapeMarkdownMetachars(value) {
10
11
  return value.replace(MARKDOWN_METACHARS, "\\$&");
11
12
  }
13
+ /**
14
+ * Renders a single resolved reference status to its display string. A reference
15
+ * with no status at this point (e.g. an in-chart sibling not present on the
16
+ * hovered series) is surfaced as unretrievable rather than masked as "no data".
17
+ */
18
+ function renderReference(ref, strings) {
19
+ switch (ref?.kind) {
20
+ case "value":
21
+ return ref.text;
22
+ case "empty":
23
+ return strings.noData;
24
+ case "multiple":
25
+ return strings.multipleItems;
26
+ case undefined:
27
+ // Absent reference → couldn't be resolved at this point.
28
+ return strings.noFetch;
29
+ }
30
+ }
12
31
  /**
13
32
  * Substitutes `{metric/id}` and `{label/id}` references in markdown content
14
33
  * with resolved values from the lookup table.
@@ -19,22 +38,22 @@ function escapeMarkdownMetachars(value) {
19
38
  * unintended formatting. `markdownToHtml` understands the backslash escapes.
20
39
  *
21
40
  * @param content - Markdown content with reference placeholders
22
- * @param values - Lookup of `metric/id` and `label/id` keys to formatted values.
41
+ * @param values - Lookup of `metric/id` and `label/id` keys to resolved statuses.
23
42
  * Keys must use a lowercase prefix; LDM identifiers are case-sensitive.
24
- * @param fallbackText - Localized text shown when a reference is recognized
25
- * but no value is available (unknown identifier, point with no value, etc.).
43
+ * @param strings - Localized placeholders for the non-value states (no data,
44
+ * multiple items, could-not-retrieve).
26
45
  *
27
46
  * @internal
28
47
  */
29
- export function resolveReferences(content, values, fallbackText) {
48
+ export function resolveReferences(content, values, strings) {
30
49
  if (!content) {
31
50
  return "";
32
51
  }
33
52
  return content.replace(REFERENCE_REGEX_MATCH, (_fullMatch, _wrapped, _key, prefix, identifier) => {
34
- // The regex matches the prefix case-insensitively; storage uses
35
- // lowercase prefixes so users can write either `{metric/foo}` or
36
- // `{Metric/foo}`. LDM identifiers stay as-is — they are case-sensitive.
37
- const value = values[`${prefix.toLowerCase()}/${identifier}`];
38
- return escapeMarkdownMetachars(value === undefined ? fallbackText : value);
53
+ // The regex captures the prefix case-insensitively (`metric` | `label`);
54
+ // route through the key helpers so the lookup format stays single-sourced
55
+ // with the write sites. LDM identifiers stay as-is — they are case-sensitive.
56
+ const key = prefix.toLowerCase() === "metric" ? metricKey(identifier) : labelKey(identifier);
57
+ return escapeMarkdownMetachars(renderReference(values[key], strings));
39
58
  });
40
59
  }
@@ -0,0 +1,22 @@
1
+ import { type ISeparators } from "@gooddata/sdk-model";
2
+ import { type ResolvedReference } from "./types.js";
3
+ /**
4
+ * Maps a raw measure value to a {@link ResolvedReference} status: `null`/
5
+ * `undefined` → empty ("No data"); otherwise the formatted (or stringified)
6
+ * value. Shared by every tooltip resolver so the value→status mapping has a
7
+ * single home. Callers whose source can yield non-numeric / non-finite values
8
+ * (e.g. geo feature payloads) normalize those to `null` before calling.
9
+ *
10
+ * @internal
11
+ */
12
+ export declare function measureReference(rawValue: number | string | null | undefined, format: string | undefined, separators?: ISeparators): ResolvedReference;
13
+ /**
14
+ * Maps a label/attribute display value to a {@link ResolvedReference} status:
15
+ * `null` / `undefined` / empty string → empty ("No data"); otherwise the value.
16
+ * Shared by every tooltip resolver so empty-value handling can't drift between
17
+ * the in-chart and external paths. The count-based "multiple" case stays with
18
+ * the caller (only the lookup builder knows the per-row count).
19
+ *
20
+ * @internal
21
+ */
22
+ export declare function labelReference(value: string | number | null | undefined): ResolvedReference;
@@ -0,0 +1,33 @@
1
+ // (C) 2026 GoodData Corporation
2
+ import { ClientFormatterFacade } from "@gooddata/number-formatter";
3
+ /**
4
+ * Maps a raw measure value to a {@link ResolvedReference} status: `null`/
5
+ * `undefined` → empty ("No data"); otherwise the formatted (or stringified)
6
+ * value. Shared by every tooltip resolver so the value→status mapping has a
7
+ * single home. Callers whose source can yield non-numeric / non-finite values
8
+ * (e.g. geo feature payloads) normalize those to `null` before calling.
9
+ *
10
+ * @internal
11
+ */
12
+ export function measureReference(rawValue, format, separators) {
13
+ if (rawValue == null) {
14
+ return { kind: "empty" };
15
+ }
16
+ if (format) {
17
+ const { formattedValue } = ClientFormatterFacade.formatValue(Number(rawValue), format, separators);
18
+ return { kind: "value", text: formattedValue };
19
+ }
20
+ return { kind: "value", text: String(rawValue) };
21
+ }
22
+ /**
23
+ * Maps a label/attribute display value to a {@link ResolvedReference} status:
24
+ * `null` / `undefined` / empty string → empty ("No data"); otherwise the value.
25
+ * Shared by every tooltip resolver so empty-value handling can't drift between
26
+ * the in-chart and external paths. The count-based "multiple" case stays with
27
+ * the caller (only the lookup builder knows the per-row count).
28
+ *
29
+ * @internal
30
+ */
31
+ export function labelReference(value) {
32
+ return value == null || value === "" ? { kind: "empty" } : { kind: "value", text: String(value) };
33
+ }
@@ -23,6 +23,21 @@ export interface ITooltipExecutionBundle {
23
23
  execution: IPreparedExecution;
24
24
  meta: ITooltipExecutionMeta;
25
25
  }
26
+ /**
27
+ * A tooltip execution plan: one batched execution for all external references,
28
+ * plus per-reference bundles used as an isolation fallback. When the batch
29
+ * rejects (e.g. a single invalid reference 400s the whole AFM), the consumer
30
+ * re-runs the per-reference bundles so one bad reference can't suppress the
31
+ * rest. Both Highcharts and geo fan out this way. `perRef` is a thunk: the
32
+ * bundles are built lazily, only when the batch fails, so the success path
33
+ * pays nothing for it.
34
+ *
35
+ * @internal
36
+ */
37
+ export interface ITooltipExecution {
38
+ batch: ITooltipExecutionBundle;
39
+ perRef: () => readonly ITooltipExecutionBundle[];
40
+ }
26
41
  /**
27
42
  * @internal
28
43
  */
@@ -37,7 +52,9 @@ export interface IBuildTooltipExecutionOptions {
37
52
  /**
38
53
  * Returns `null` when the content has no references or all references are
39
54
  * already in the chart (resolvable from drill data without a secondary call).
55
+ * Otherwise returns the batched execution plus per-reference bundles for the
56
+ * fan-out fallback (see {@link ITooltipExecution}).
40
57
  *
41
58
  * @internal
42
59
  */
43
- export declare function buildTooltipExecution(executionFactory: IExecutionFactory, chartDefinition: IExecutionDefinition, tooltipContent: string, options?: IBuildTooltipExecutionOptions): ITooltipExecutionBundle | null;
60
+ export declare function buildTooltipExecution(executionFactory: IExecutionFactory, chartDefinition: IExecutionDefinition, tooltipContent: string, options?: IBuildTooltipExecutionOptions): ITooltipExecution | null;
@@ -67,26 +67,27 @@ function getFilterDependencyMeasures(filters, chartMeasures) {
67
67
  return chartMeasures.filter((m) => neededLocalIds.has(measureLocalId(m)));
68
68
  }
69
69
  /**
70
- * Build tooltip-only measures for refs not already in the chart.
71
- * Labels get a max+count pair (mirrors the RichText widget pattern) so the
72
- * lookup can render "(Multiple items)" when a label resolves to >1 value per row.
70
+ * Build tooltip-only measures for the given references (already filtered to
71
+ * those not in the chart). Labels get a max+count pair (mirrors the RichText
72
+ * widget pattern) so the lookup can render "(Multiple items)" when a label
73
+ * resolves to >1 value per row.
73
74
  *
74
75
  * LocalId prefixes `tt_m_`, `tt_lv_`, `tt_lc_` are reserved — collision with
75
76
  * chart-side measure localIds would break filter-dependency reuse.
76
77
  */
77
- function buildTooltipItems(refs, chartMetricIds, chartLabelIds) {
78
+ function buildTooltipItems(refs) {
78
79
  const measures = [];
79
80
  const labelCountMap = {};
80
81
  const labelIdMap = {};
81
82
  const measureIdMap = {};
82
83
  let idx = 0;
83
84
  for (const ref of refs) {
84
- if (ref.type === "metric" && !chartMetricIds.has(ref.id)) {
85
+ if (ref.type === "metric") {
85
86
  const localId = `tt_m_${idx++}`;
86
87
  measures.push(newMeasure(idRef(ref.id, "measure"), (m) => m.localId(localId)));
87
88
  measureIdMap[localId] = ref.id;
88
89
  }
89
- else if (ref.type === "label" && !chartLabelIds.has(ref.id)) {
90
+ else {
90
91
  const valueLocalId = `tt_lv_${idx}`;
91
92
  const countLocalId = `tt_lc_${idx}`;
92
93
  idx++;
@@ -119,19 +120,12 @@ function getSlicingAttributes(definition, slicingAttributeLocalIds) {
119
120
  return out;
120
121
  }
121
122
  /**
122
- * Returns `null` when the content has no references or all references are
123
- * already in the chart (resolvable from drill data without a secondary call).
124
- *
125
- * @internal
123
+ * Builds a single execution bundle for the given external references (slicing
124
+ * attributes + tooltip measures + filter-dependency measures). Returns `null`
125
+ * when there are no measures to fetch.
126
126
  */
127
- export function buildTooltipExecution(executionFactory, chartDefinition, tooltipContent, options) {
128
- const refs = parseReferences(tooltipContent);
129
- if (refs.length === 0) {
130
- return null;
131
- }
132
- const chartMetricIds = getChartMetricIds(chartDefinition);
133
- const chartLabelIds = getChartLabelIds(chartDefinition);
134
- const { measures, labelCountMap, labelIdMap, measureIdMap } = buildTooltipItems(refs, chartMetricIds, chartLabelIds);
127
+ function buildBundle(executionFactory, chartDefinition, externalRefs, options) {
128
+ const { measures, labelCountMap, labelIdMap, measureIdMap } = buildTooltipItems(externalRefs);
135
129
  if (measures.length === 0) {
136
130
  return null;
137
131
  }
@@ -150,3 +144,32 @@ export function buildTooltipExecution(executionFactory, chartDefinition, tooltip
150
144
  meta: { labelCountMap, measureIdMap, labelIdMap },
151
145
  };
152
146
  }
147
+ /**
148
+ * Returns `null` when the content has no references or all references are
149
+ * already in the chart (resolvable from drill data without a secondary call).
150
+ * Otherwise returns the batched execution plus per-reference bundles for the
151
+ * fan-out fallback (see {@link ITooltipExecution}).
152
+ *
153
+ * @internal
154
+ */
155
+ export function buildTooltipExecution(executionFactory, chartDefinition, tooltipContent, options) {
156
+ const refs = parseReferences(tooltipContent);
157
+ if (refs.length === 0) {
158
+ return null;
159
+ }
160
+ const chartMetricIds = getChartMetricIds(chartDefinition);
161
+ const chartLabelIds = getChartLabelIds(chartDefinition);
162
+ // References not already resolvable from the chart's own drill data.
163
+ const externalRefs = refs.filter((ref) => (ref.type === "metric" && !chartMetricIds.has(ref.id)) ||
164
+ (ref.type === "label" && !chartLabelIds.has(ref.id)));
165
+ const batch = buildBundle(executionFactory, chartDefinition, externalRefs, options);
166
+ if (!batch) {
167
+ return null;
168
+ }
169
+ // Lazy: built only on batch failure (Highcharts and geo both fan out); the
170
+ // success path never invokes it.
171
+ const perRef = () => externalRefs
172
+ .map((ref) => buildBundle(executionFactory, chartDefinition, [ref], options))
173
+ .filter((bundle) => bundle !== null);
174
+ return { batch, perRef };
175
+ }
@@ -2,20 +2,12 @@ import { type IDataView } from "@gooddata/sdk-backend-spi";
2
2
  import { type ISeparators } from "@gooddata/sdk-model";
3
3
  import { type ITooltipExecutionMeta } from "./tooltipExecution.js";
4
4
  import { type IResolvedReferenceValues } from "./types.js";
5
- /**
6
- * Localized placeholders for unresolved reference values. Mirrors the RichText
7
- * widget's `richText.no_data` / `richText.multiple_data` messages.
8
- *
9
- * @internal
10
- */
11
- export interface ITooltipLookupLocalizedStrings {
12
- noData: string;
13
- multipleItems: string;
14
- }
15
5
  /**
16
6
  * Build a per-data-point lookup keyed by `${displayFormId}:${uri}` segments
17
7
  * (joined by `|`, sorted). Iteration is orientation-agnostic via slices/series.
8
+ * Each reference is tagged with a {@link ResolvedReference} status; localized
9
+ * placeholder strings are applied later, at the render site.
18
10
  *
19
11
  * @internal
20
12
  */
21
- export declare function buildLookupTable(dataView: IDataView, meta: ITooltipExecutionMeta, separators?: ISeparators, localizedStrings?: ITooltipLookupLocalizedStrings): Map<string, IResolvedReferenceValues>;
13
+ export declare function buildLookupTable(dataView: IDataView, meta: ITooltipExecutionMeta, separators?: ISeparators): Map<string, IResolvedReferenceValues>;
@@ -1,19 +1,18 @@
1
1
  // (C) 2026 GoodData Corporation
2
- import { ClientFormatterFacade } from "@gooddata/number-formatter";
3
2
  import { isResultAttributeHeader } from "@gooddata/sdk-model";
4
3
  import { DataViewFacade } from "@gooddata/sdk-ui";
4
+ import { labelReference, measureReference } from "./referenceStatus.js";
5
5
  import { buildKeySegment, joinKeySegments } from "./tooltipKey.js";
6
- const DEFAULT_LOCALIZED_STRINGS = {
7
- noData: "(No data)",
8
- multipleItems: "(Multiple items)",
9
- };
6
+ import { labelKey, metricKey } from "./types.js";
10
7
  /**
11
8
  * Build a per-data-point lookup keyed by `${displayFormId}:${uri}` segments
12
9
  * (joined by `|`, sorted). Iteration is orientation-agnostic via slices/series.
10
+ * Each reference is tagged with a {@link ResolvedReference} status; localized
11
+ * placeholder strings are applied later, at the render site.
13
12
  *
14
13
  * @internal
15
14
  */
16
- export function buildLookupTable(dataView, meta, separators, localizedStrings = DEFAULT_LOCALIZED_STRINGS) {
15
+ export function buildLookupTable(dataView, meta, separators) {
17
16
  const lookup = new Map();
18
17
  const facade = DataViewFacade.for(dataView);
19
18
  const slices = facade.data().slices().toArray();
@@ -51,30 +50,14 @@ export function buildLookupTable(dataView, meta, separators, localizedStrings =
51
50
  const countValue = countSeries
52
51
  ? Number(countSeries.dataPoints()[sliceIdx]?.rawValue ?? 0)
53
52
  : 1;
54
- if (countValue > 1) {
55
- values[`label/${labelId}`] = localizedStrings.multipleItems;
56
- }
57
- else if (rawValue == null || rawValue === "") {
58
- values[`label/${labelId}`] = localizedStrings.noData;
59
- }
60
- else {
61
- values[`label/${labelId}`] = String(rawValue);
62
- }
53
+ // count > 1 → "(Multiple items)"; else empty/value via the shared helper.
54
+ values[labelKey(labelId)] = countValue > 1 ? { kind: "multiple" } : labelReference(rawValue);
63
55
  continue;
64
56
  }
65
57
  const metricId = meta.measureIdMap[localId];
66
58
  if (metricId) {
67
59
  const format = measureDesc.measureHeaderItem.format;
68
- if (rawValue == null) {
69
- values[`metric/${metricId}`] = localizedStrings.noData;
70
- }
71
- else if (format) {
72
- const { formattedValue } = ClientFormatterFacade.formatValue(Number(rawValue), format, separators);
73
- values[`metric/${metricId}`] = formattedValue;
74
- }
75
- else {
76
- values[`metric/${metricId}`] = String(rawValue);
77
- }
60
+ values[metricKey(metricId)] = measureReference(rawValue, format, separators);
78
61
  }
79
62
  }
80
63
  lookup.set(pointKey, values);
@@ -59,10 +59,56 @@ export interface ICustomTooltipConfig {
59
59
  placement?: CustomTooltipPlacement;
60
60
  }
61
61
  /**
62
- * Lookup of resolved reference values keyed by `metric/id` or `label/id`.
62
+ * Resolution outcome for a single `{metric/id}` / `{label/id}` reference at a
63
+ * data point. A discriminated union so the renderer maps each state to its own
64
+ * localized message instead of collapsing them: `empty` → "(No data)",
65
+ * `multiple` → "(Multiple items)". A reference that couldn't be resolved at all
66
+ * is represented by *absence* from the lookup (`undefined`), which the renderer
67
+ * maps to "(Data could not be retrieved)" — there is no explicit error variant.
68
+ *
69
+ * @internal
70
+ */
71
+ export type ResolvedReference = {
72
+ readonly kind: "value";
73
+ readonly text: string;
74
+ } | {
75
+ readonly kind: "empty";
76
+ } | {
77
+ readonly kind: "multiple";
78
+ };
79
+ /**
80
+ * Lookup of resolved reference statuses keyed by `metric/id` or `label/id`.
63
81
  *
64
82
  * @internal
65
83
  */
66
84
  export interface IResolvedReferenceValues {
67
- [referenceKey: string]: string | undefined;
85
+ [referenceKey: string]: ResolvedReference | undefined;
86
+ }
87
+ /**
88
+ * Builds the `metric/<id>` lookup key for a metric reference. A reference key
89
+ * has exactly two shapes ({@link metricKey}, {@link labelKey}); keeping the
90
+ * convention here makes it the single source for both the write sites and the
91
+ * read in `resolveReferences` (which routes the parsed prefix through these
92
+ * helpers rather than rebuilding the key).
93
+ *
94
+ * @internal
95
+ */
96
+ export declare const metricKey: (id: string) => string;
97
+ /**
98
+ * Builds the `label/<id>` lookup key for a label reference. See {@link metricKey}.
99
+ *
100
+ * @internal
101
+ */
102
+ export declare const labelKey: (id: string) => string;
103
+ /**
104
+ * Localized placeholder strings for the non-value reference states. Built once
105
+ * at the render site (where `intl` is available) and threaded into
106
+ * `resolveReferences`, so reference resolution stays free of i18n concerns.
107
+ *
108
+ * @internal
109
+ */
110
+ export interface ITooltipLocalizedStrings {
111
+ readonly noData: string;
112
+ readonly multipleItems: string;
113
+ readonly noFetch: string;
68
114
  }
@@ -1,2 +1,17 @@
1
1
  // (C) 2026 GoodData Corporation
2
- export {};
2
+ /**
3
+ * Builds the `metric/<id>` lookup key for a metric reference. A reference key
4
+ * has exactly two shapes ({@link metricKey}, {@link labelKey}); keeping the
5
+ * convention here makes it the single source for both the write sites and the
6
+ * read in `resolveReferences` (which routes the parsed prefix through these
7
+ * helpers rather than rebuilding the key).
8
+ *
9
+ * @internal
10
+ */
11
+ export const metricKey = (id) => `metric/${id}`;
12
+ /**
13
+ * Builds the `label/<id>` lookup key for a label reference. See {@link metricKey}.
14
+ *
15
+ * @internal
16
+ */
17
+ export const labelKey = (id) => `label/${id}`;
@@ -1,22 +1,29 @@
1
- import { type IPreparedExecution } from "@gooddata/sdk-backend-spi";
2
1
  import { type ISeparators } from "@gooddata/sdk-model";
3
- import { type ITooltipExecutionBundle, type ITooltipExecutionMeta } from "./tooltipExecution.js";
4
- import { type ITooltipLookupLocalizedStrings } from "./tooltipLookup.js";
2
+ import { type ITooltipExecution } from "./tooltipExecution.js";
5
3
  import { type IResolvedReferenceValues } from "./types.js";
6
4
  /**
7
- * One prepared tooltip execution paired with a caller-owned key and the
8
- * context that travels with the built lookup.
5
+ * Single-execution variant for chart families that have one tooltip execution
6
+ * per chart (e.g. Highcharts). Returns `undefined` while no execution is
7
+ * provided or before the first result lands; consumers handle that as "no
8
+ * external values".
9
+ *
10
+ * @internal
11
+ */
12
+ export declare function useTooltipLookup(execution: ITooltipExecution | undefined, separators?: ISeparators): Map<string, IResolvedReferenceValues> | undefined;
13
+ /**
14
+ * One tooltip execution plan paired with a caller-owned key and the context
15
+ * that travels with the built lookup.
9
16
  *
10
17
  * @internal
11
18
  */
12
19
  export interface ITooltipLookupExecutionEntry<TContext> {
13
20
  key: string;
14
- execution: IPreparedExecution;
15
- meta: ITooltipExecutionMeta;
21
+ execution: ITooltipExecution;
16
22
  context: TContext;
17
23
  }
18
24
  /**
19
- * Built lookup for one tooltip execution entry.
25
+ * Built lookup for one tooltip execution entry (per-entry fan-out, mirroring
26
+ * the single-execution variant).
20
27
  *
21
28
  * @internal
22
29
  */
@@ -25,19 +32,11 @@ export interface ITooltipLookupExecutionResult<TContext> {
25
32
  context: TContext;
26
33
  }
27
34
  /**
28
- * Single-bundle variant for chart families that have one tooltip execution per
29
- * chart (e.g. Highcharts). Returns `undefined` while no bundle is provided or
30
- * before the first result lands; consumers handle that as "no external values".
31
- *
32
- * @internal
33
- */
34
- export declare function useTooltipLookup(bundle: ITooltipExecutionBundle | undefined, separators?: ISeparators, localizedStrings?: ITooltipLookupLocalizedStrings): Map<string, IResolvedReferenceValues> | undefined;
35
- /**
36
- * Multi-bundle variant for chart families that key tooltip executions per
37
- * sub-target (e.g. geo per-layer). `context` is required so the produced
38
- * lookup carries whatever the caller needs to interpret the result —
39
- * downstream code does not have to defensively check for missing context.
35
+ * Multi-execution variant for chart families that key tooltip executions per
36
+ * sub-target (e.g. geo per-layer). Each entry runs through the same batch
37
+ * per-reference fan-out as the single-execution variant. `context` travels with
38
+ * the built lookup so downstream code needn't defensively check for it.
40
39
  *
41
40
  * @internal
42
41
  */
43
- export declare function useTooltipLookupExecutions<TContext>(entries: readonly ITooltipLookupExecutionEntry<TContext>[], separators?: ISeparators, localizedStrings?: ITooltipLookupLocalizedStrings): Map<string, ITooltipLookupExecutionResult<TContext>>;
42
+ export declare function useTooltipLookupExecutions<TContext>(entries: readonly ITooltipLookupExecutionEntry<TContext>[], separators?: ISeparators): Map<string, ITooltipLookupExecutionResult<TContext>>;
@@ -2,47 +2,93 @@
2
2
  import { useMemo } from "react";
3
3
  import { useCancelablePromise } from "@gooddata/sdk-ui";
4
4
  import { buildLookupTable } from "./tooltipLookup.js";
5
- const EMPTY_LOOKUPS = new Map();
6
5
  async function executeOne(execution) {
7
6
  const result = await execution.execute();
8
7
  return result.readAll();
9
8
  }
10
- function getEntriesFingerprint(entries) {
11
- return entries.map((entry) => `${entry.key}::${entry.execution.fingerprint()}`).join("||");
9
+ /**
10
+ * Execute the batch; on failure, fan out to per-reference bundles so a single
11
+ * bad reference (e.g. one that 400s the batched AFM) only takes itself down
12
+ * while the rest still resolve. Isolation is structural — a failed reference is
13
+ * left out of the returned inputs and renders the unresolved default, with no
14
+ * parsing of the backend error. Fan-out fires on *any* batch rejection (a
15
+ * transient 5xx/timeout too, not just a bad-ref 400), so a flaky backend can
16
+ * produce N follow-up executions per plan.
17
+ */
18
+ async function runWithFallback(execution) {
19
+ try {
20
+ const dataView = await executeOne(execution.batch.execution);
21
+ return [{ dataView, meta: execution.batch.meta }];
22
+ }
23
+ catch {
24
+ // Build the per-reference bundles now (lazy) and keep the ones that resolve.
25
+ const perRef = execution.perRef();
26
+ const settled = await Promise.allSettled(perRef.map((bundle) => executeOne(bundle.execution)));
27
+ const inputs = [];
28
+ settled.forEach((result, index) => {
29
+ if (result.status === "fulfilled") {
30
+ inputs.push({ dataView: result.value, meta: perRef[index].meta });
31
+ }
32
+ });
33
+ return inputs;
34
+ }
12
35
  }
13
- async function executeAll(entries) {
14
- const settled = await Promise.allSettled(entries.map(async (entry) => ({ ...entry, dataView: await executeOne(entry.execution) })));
15
- // Failed entries drop out silently; callers fall back when a lookup is missing.
16
- return settled.flatMap((result) => (result.status === "fulfilled" ? [result.value] : []));
36
+ /**
37
+ * Merge per-execution lookups into one map (by point key, shallow-merging the
38
+ * per-point reference statuses). Built in a memo so a separators change rebuilds
39
+ * without re-executing.
40
+ */
41
+ function buildLookup(inputs, separators) {
42
+ const lookup = new Map();
43
+ for (const { dataView, meta } of inputs) {
44
+ for (const [pointKey, values] of buildLookupTable(dataView, meta, separators)) {
45
+ const existing = lookup.get(pointKey);
46
+ lookup.set(pointKey, existing ? { ...existing, ...values } : values);
47
+ }
48
+ }
49
+ return lookup;
17
50
  }
18
51
  /**
19
- * Single-bundle variant for chart families that have one tooltip execution per
20
- * chart (e.g. Highcharts). Returns `undefined` while no bundle is provided or
21
- * before the first result lands; consumers handle that as "no external values".
52
+ * Single-execution variant for chart families that have one tooltip execution
53
+ * per chart (e.g. Highcharts). Returns `undefined` while no execution is
54
+ * provided or before the first result lands; consumers handle that as "no
55
+ * external values".
22
56
  *
23
57
  * @internal
24
58
  */
25
- export function useTooltipLookup(bundle, separators, localizedStrings) {
26
- const fingerprint = bundle?.execution.fingerprint();
59
+ export function useTooltipLookup(execution, separators) {
60
+ const fingerprint = execution?.batch.execution.fingerprint();
27
61
  const { result } = useCancelablePromise({
28
- promise: bundle ? () => executeOne(bundle.execution) : undefined,
62
+ promise: execution ? () => runWithFallback(execution) : undefined,
29
63
  }, [fingerprint]);
30
- return useMemo(() => {
31
- if (!result || !bundle) {
32
- return undefined;
33
- }
34
- return buildLookupTable(result, bundle.meta, separators, localizedStrings);
35
- }, [result, bundle, separators, localizedStrings]);
64
+ return useMemo(() => (result ? buildLookup(result, separators) : undefined), [result, separators]);
65
+ }
66
+ const EMPTY_LOOKUPS = new Map();
67
+ function getEntriesFingerprint(entries) {
68
+ return entries
69
+ .map((entry) => `${entry.key}::${entry.execution.batch.execution.fingerprint()}`)
70
+ .join("||");
71
+ }
72
+ async function executeAll(entries) {
73
+ // runWithFallback resolves for any execution failure (a failed reference is
74
+ // just omitted), so allSettled only guards a stray throw — one layer can't
75
+ // drop the rest.
76
+ const settled = await Promise.allSettled(entries.map(async (entry) => ({
77
+ key: entry.key,
78
+ inputs: await runWithFallback(entry.execution),
79
+ context: entry.context,
80
+ })));
81
+ return settled.flatMap((result) => (result.status === "fulfilled" ? [result.value] : []));
36
82
  }
37
83
  /**
38
- * Multi-bundle variant for chart families that key tooltip executions per
39
- * sub-target (e.g. geo per-layer). `context` is required so the produced
40
- * lookup carries whatever the caller needs to interpret the result —
41
- * downstream code does not have to defensively check for missing context.
84
+ * Multi-execution variant for chart families that key tooltip executions per
85
+ * sub-target (e.g. geo per-layer). Each entry runs through the same batch →
86
+ * per-reference fan-out as the single-execution variant. `context` travels with
87
+ * the built lookup so downstream code needn't defensively check for it.
42
88
  *
43
89
  * @internal
44
90
  */
45
- export function useTooltipLookupExecutions(entries, separators, localizedStrings) {
91
+ export function useTooltipLookupExecutions(entries, separators) {
46
92
  const fingerprint = getEntriesFingerprint(entries);
47
93
  const { result } = useCancelablePromise({
48
94
  promise: entries.length > 0 ? () => executeAll(entries) : undefined,
@@ -53,11 +99,9 @@ export function useTooltipLookupExecutions(entries, separators, localizedStrings
53
99
  }
54
100
  const lookups = new Map();
55
101
  for (const entry of result) {
56
- lookups.set(entry.key, {
57
- lookup: buildLookupTable(entry.dataView, entry.meta, separators, localizedStrings),
58
- context: entry.context,
59
- });
102
+ const lookup = buildLookup(entry.inputs, separators);
103
+ lookups.set(entry.key, { lookup, context: entry.context });
60
104
  }
61
105
  return lookups;
62
- }, [result, separators, localizedStrings]);
106
+ }, [result, separators]);
63
107
  }
package/esm/index.d.ts CHANGED
@@ -28,12 +28,13 @@ export { getHeadlineResponsiveClassName } from "./utils/headlineResponsiveClassN
28
28
  export { getLegendDetails, type ILegendDetails, type ILegendDetailOptions, } from "./legend/PopUpLegend/helpers.js";
29
29
  export { PATTERN_FILLS, getPatternFillByIndex, getPatternFillByName, getPatternFill, type IPatternFill, type PatternFillName, type IChartFillConfig, } from "./coloring/patternFills.js";
30
30
  export { PatternFill, type IPatternFillProps } from "./coloring/PatternFill.js";
31
- export { type ICustomTooltipConfig, type CustomTooltipPlacement, type IResolvedReferenceValues, } from "./customTooltip/types.js";
31
+ export { type ICustomTooltipConfig, type CustomTooltipPlacement, type IResolvedReferenceValues, type ResolvedReference, type ITooltipLocalizedStrings, metricKey, labelKey, } from "./customTooltip/types.js";
32
32
  export { markdownToHtml } from "./customTooltip/markdownToHtml.js";
33
33
  export { resolveReferences } from "./customTooltip/referenceResolver.js";
34
+ export { measureReference, labelReference } from "./customTooltip/referenceStatus.js";
35
+ export { buildTooltipLocalizedStrings } from "./customTooltip/localizedStrings.js";
34
36
  export { resolveMeasureLdmIdentifier } from "./customTooltip/measureLdmIdentifier.js";
35
- export { buildTooltipExecution, type IBuildTooltipExecutionOptions, type ITooltipExecutionBundle, type ITooltipExecutionMeta, } from "./customTooltip/tooltipExecution.js";
36
- export { buildLookupTable, type ITooltipLookupLocalizedStrings } from "./customTooltip/tooltipLookup.js";
37
+ export { buildTooltipExecution, type IBuildTooltipExecutionOptions, type ITooltipExecution, type ITooltipExecutionBundle, type ITooltipExecutionMeta, } from "./customTooltip/tooltipExecution.js";
37
38
  export { buildKeySegment, joinKeySegments } from "./customTooltip/tooltipKey.js";
38
39
  export { composeCustomTooltipSectionHtml } from "./customTooltip/composeSectionHtml.js";
39
40
  export { useTooltipLookup, useTooltipLookupExecutions, type ITooltipLookupExecutionEntry, type ITooltipLookupExecutionResult, } from "./customTooltip/useTooltipLookupExecutions.js";
package/esm/index.js CHANGED
@@ -29,11 +29,13 @@ export { getHeadlineResponsiveClassName } from "./utils/headlineResponsiveClassN
29
29
  export { getLegendDetails, } from "./legend/PopUpLegend/helpers.js";
30
30
  export { PATTERN_FILLS, getPatternFillByIndex, getPatternFillByName, getPatternFill, } from "./coloring/patternFills.js";
31
31
  export { PatternFill } from "./coloring/PatternFill.js";
32
+ export { metricKey, labelKey, } from "./customTooltip/types.js";
32
33
  export { markdownToHtml } from "./customTooltip/markdownToHtml.js";
33
34
  export { resolveReferences } from "./customTooltip/referenceResolver.js";
35
+ export { measureReference, labelReference } from "./customTooltip/referenceStatus.js";
36
+ export { buildTooltipLocalizedStrings } from "./customTooltip/localizedStrings.js";
34
37
  export { resolveMeasureLdmIdentifier } from "./customTooltip/measureLdmIdentifier.js";
35
38
  export { buildTooltipExecution, } from "./customTooltip/tooltipExecution.js";
36
- export { buildLookupTable } from "./customTooltip/tooltipLookup.js";
37
39
  export { buildKeySegment, joinKeySegments } from "./customTooltip/tooltipKey.js";
38
40
  export { composeCustomTooltipSectionHtml } from "./customTooltip/composeSectionHtml.js";
39
41
  export { useTooltipLookup, useTooltipLookupExecutions, } from "./customTooltip/useTooltipLookupExecutions.js";
@@ -21,6 +21,7 @@ import { IExecutionFactory } from '@gooddata/sdk-backend-spi';
21
21
  import { IHeaderPredicate } from '@gooddata/sdk-ui';
22
22
  import { IMappingHeader } from '@gooddata/sdk-ui';
23
23
  import { IMeasure } from '@gooddata/sdk-model';
24
+ import { IntlShape } from 'react-intl';
24
25
  import { IPreparedExecution } from '@gooddata/sdk-backend-spi';
25
26
  import { IRgbColorValue } from '@gooddata/sdk-model';
26
27
  import { ISeparators } from '@gooddata/sdk-model';
@@ -56,20 +57,24 @@ export declare class AttributeColorStrategy extends ColorStrategy {
56
57
  export declare function buildKeySegment(displayFormId: string, uri: string): string;
57
58
 
58
59
  /**
59
- * Build a per-data-point lookup keyed by `${displayFormId}:${uri}` segments
60
- * (joined by `|`, sorted). Iteration is orientation-agnostic via slices/series.
60
+ * Returns `null` when the content has no references or all references are
61
+ * already in the chart (resolvable from drill data without a secondary call).
62
+ * Otherwise returns the batched execution plus per-reference bundles for the
63
+ * fan-out fallback (see {@link ITooltipExecution}).
61
64
  *
62
65
  * @internal
63
66
  */
64
- export declare function buildLookupTable(dataView: IDataView, meta: ITooltipExecutionMeta, separators?: ISeparators, localizedStrings?: ITooltipLookupLocalizedStrings): Map<string, IResolvedReferenceValues>;
67
+ export declare function buildTooltipExecution(executionFactory: IExecutionFactory, chartDefinition: IExecutionDefinition, tooltipContent: string, options?: IBuildTooltipExecutionOptions): ITooltipExecution | null;
65
68
 
66
69
  /**
67
- * Returns `null` when the content has no references or all references are
68
- * already in the chart (resolvable from drill data without a secondary call).
70
+ * Builds the localized placeholder strings for the non-value reference states,
71
+ * shared by every tooltip consumer (Highcharts, geo) so the wording and the
72
+ * message ids live in one place. `intl` is optional — the Highcharts tooltip
73
+ * formatter receives it optionally — and falls back to English.
69
74
  *
70
75
  * @internal
71
76
  */
72
- export declare function buildTooltipExecution(executionFactory: IExecutionFactory, chartDefinition: IExecutionDefinition, tooltipContent: string, options?: IBuildTooltipExecutionOptions): ITooltipExecutionBundle | null;
77
+ export declare function buildTooltipLocalizedStrings(intl?: IntlShape): ITooltipLocalizedStrings;
73
78
 
74
79
  /**
75
80
  * @internal
@@ -129,7 +134,7 @@ export declare const ColorUtils: {
129
134
  *
130
135
  * @internal
131
136
  */
132
- export declare function composeCustomTooltipSectionHtml(content: string, inChartValues: IResolvedReferenceValues, externalValues: IResolvedReferenceValues, fallbackText: string): string;
137
+ export declare function composeCustomTooltipSectionHtml(content: string, inChartValues: IResolvedReferenceValues, externalValues: IResolvedReferenceValues, localizedStrings: ITooltipLocalizedStrings): string;
133
138
 
134
139
  /**
135
140
  * Placement of the custom tooltip section relative to the default tooltip content.
@@ -679,12 +684,12 @@ export declare interface IRange {
679
684
  }
680
685
 
681
686
  /**
682
- * Lookup of resolved reference values keyed by `metric/id` or `label/id`.
687
+ * Lookup of resolved reference statuses keyed by `metric/id` or `label/id`.
683
688
  *
684
689
  * @internal
685
690
  */
686
691
  export declare interface IResolvedReferenceValues {
687
- [referenceKey: string]: string | undefined;
692
+ [referenceKey: string]: ResolvedReference | undefined;
688
693
  }
689
694
 
690
695
  /**
@@ -784,6 +789,22 @@ export declare function isValidMappedColor(colorItem: IColor | null | undefined,
784
789
  */
785
790
  export declare type ItemBorderRadiusPredicate = (item: any) => boolean;
786
791
 
792
+ /**
793
+ * A tooltip execution plan: one batched execution for all external references,
794
+ * plus per-reference bundles used as an isolation fallback. When the batch
795
+ * rejects (e.g. a single invalid reference 400s the whole AFM), the consumer
796
+ * re-runs the per-reference bundles so one bad reference can't suppress the
797
+ * rest. Both Highcharts and geo fan out this way. `perRef` is a thunk: the
798
+ * bundles are built lazily, only when the batch fails, so the success path
799
+ * pays nothing for it.
800
+ *
801
+ * @internal
802
+ */
803
+ export declare interface ITooltipExecution {
804
+ batch: ITooltipExecutionBundle;
805
+ perRef: () => readonly ITooltipExecutionBundle[];
806
+ }
807
+
787
808
  /**
788
809
  * Prepared tooltip execution paired with the meta needed to interpret its result.
789
810
  * Carry them together — meta from one call mis-interprets results from another.
@@ -810,20 +831,33 @@ export declare interface ITooltipExecutionMeta {
810
831
  }
811
832
 
812
833
  /**
813
- * One prepared tooltip execution paired with a caller-owned key and the
814
- * context that travels with the built lookup.
834
+ * Localized placeholder strings for the non-value reference states. Built once
835
+ * at the render site (where `intl` is available) and threaded into
836
+ * `resolveReferences`, so reference resolution stays free of i18n concerns.
837
+ *
838
+ * @internal
839
+ */
840
+ export declare interface ITooltipLocalizedStrings {
841
+ readonly noData: string;
842
+ readonly multipleItems: string;
843
+ readonly noFetch: string;
844
+ }
845
+
846
+ /**
847
+ * One tooltip execution plan paired with a caller-owned key and the context
848
+ * that travels with the built lookup.
815
849
  *
816
850
  * @internal
817
851
  */
818
852
  export declare interface ITooltipLookupExecutionEntry<TContext> {
819
853
  key: string;
820
- execution: IPreparedExecution;
821
- meta: ITooltipExecutionMeta;
854
+ execution: ITooltipExecution;
822
855
  context: TContext;
823
856
  }
824
857
 
825
858
  /**
826
- * Built lookup for one tooltip execution entry.
859
+ * Built lookup for one tooltip execution entry (per-entry fan-out, mirroring
860
+ * the single-execution variant).
827
861
  *
828
862
  * @internal
829
863
  */
@@ -833,20 +867,27 @@ export declare interface ITooltipLookupExecutionResult<TContext> {
833
867
  }
834
868
 
835
869
  /**
836
- * Localized placeholders for unresolved reference values. Mirrors the RichText
837
- * widget's `richText.no_data` / `richText.multiple_data` messages.
870
+ * @internal
871
+ */
872
+ export declare function joinKeySegments(parts: readonly string[]): string;
873
+
874
+ /**
875
+ * Builds the `label/<id>` lookup key for a label reference. See {@link metricKey}.
838
876
  *
839
877
  * @internal
840
878
  */
841
- export declare interface ITooltipLookupLocalizedStrings {
842
- noData: string;
843
- multipleItems: string;
844
- }
879
+ export declare const labelKey: (id: string) => string;
845
880
 
846
881
  /**
882
+ * Maps a label/attribute display value to a {@link ResolvedReference} status:
883
+ * `null` / `undefined` / empty string → empty ("No data"); otherwise the value.
884
+ * Shared by every tooltip resolver so empty-value handling can't drift between
885
+ * the in-chart and external paths. The count-based "multiple" case stays with
886
+ * the caller (only the lookup builder knows the per-row count).
887
+ *
847
888
  * @internal
848
889
  */
849
- export declare function joinKeySegments(parts: readonly string[]): string;
890
+ export declare function labelReference(value: string | number | null | undefined): ResolvedReference;
850
891
 
851
892
  /**
852
893
  * @internal
@@ -885,6 +926,28 @@ export declare const LegendPosition: {
885
926
  */
886
927
  export declare function markdownToHtml(markdown: string): string;
887
928
 
929
+ /**
930
+ * Maps a raw measure value to a {@link ResolvedReference} status: `null`/
931
+ * `undefined` → empty ("No data"); otherwise the formatted (or stringified)
932
+ * value. Shared by every tooltip resolver so the value→status mapping has a
933
+ * single home. Callers whose source can yield non-numeric / non-finite values
934
+ * (e.g. geo feature payloads) normalize those to `null` before calling.
935
+ *
936
+ * @internal
937
+ */
938
+ export declare function measureReference(rawValue: number | string | null | undefined, format: string | undefined, separators?: ISeparators): ResolvedReference;
939
+
940
+ /**
941
+ * Builds the `metric/<id>` lookup key for a metric reference. A reference key
942
+ * has exactly two shapes ({@link metricKey}, {@link labelKey}); keeping the
943
+ * convention here makes it the single source for both the write sites and the
944
+ * read in `resolveReferences` (which routes the parsed prefix through these
945
+ * helpers rather than rebuilding the key).
946
+ *
947
+ * @internal
948
+ */
949
+ export declare const metricKey: (id: string) => string;
950
+
888
951
  /**
889
952
  * @internal
890
953
  */
@@ -947,6 +1010,25 @@ export declare function PopUpLegend({ name, maxRows, enableBorderRadius, series,
947
1010
  */
948
1011
  export declare type PositionType = "left" | "right" | "top" | "bottom" | "auto";
949
1012
 
1013
+ /**
1014
+ * Resolution outcome for a single `{metric/id}` / `{label/id}` reference at a
1015
+ * data point. A discriminated union so the renderer maps each state to its own
1016
+ * localized message instead of collapsing them: `empty` → "(No data)",
1017
+ * `multiple` → "(Multiple items)". A reference that couldn't be resolved at all
1018
+ * is represented by *absence* from the lookup (`undefined`), which the renderer
1019
+ * maps to "(Data could not be retrieved)" — there is no explicit error variant.
1020
+ *
1021
+ * @internal
1022
+ */
1023
+ export declare type ResolvedReference = {
1024
+ readonly kind: "value";
1025
+ readonly text: string;
1026
+ } | {
1027
+ readonly kind: "empty";
1028
+ } | {
1029
+ readonly kind: "multiple";
1030
+ };
1031
+
950
1032
  /**
951
1033
  * Returns the LDM identifier the measure ultimately resolves to.
952
1034
  *
@@ -968,14 +1050,14 @@ export declare function resolveMeasureLdmIdentifier(measure: IMeasure, allMeasur
968
1050
  * unintended formatting. `markdownToHtml` understands the backslash escapes.
969
1051
  *
970
1052
  * @param content - Markdown content with reference placeholders
971
- * @param values - Lookup of `metric/id` and `label/id` keys to formatted values.
1053
+ * @param values - Lookup of `metric/id` and `label/id` keys to resolved statuses.
972
1054
  * Keys must use a lowercase prefix; LDM identifiers are case-sensitive.
973
- * @param fallbackText - Localized text shown when a reference is recognized
974
- * but no value is available (unknown identifier, point with no value, etc.).
1055
+ * @param strings - Localized placeholders for the non-value states (no data,
1056
+ * multiple items, could-not-retrieve).
975
1057
  *
976
1058
  * @internal
977
1059
  */
978
- export declare function resolveReferences(content: string, values: IResolvedReferenceValues, fallbackText: string): string;
1060
+ export declare function resolveReferences(content: string, values: IResolvedReferenceValues, strings: ITooltipLocalizedStrings): string;
979
1061
 
980
1062
  /**
981
1063
  * @internal
@@ -999,23 +1081,24 @@ export declare const StaticLegend: NamedExoticComponent<IStaticLegendProps>;
999
1081
  export declare const SupportedLegendPositions: PositionType[];
1000
1082
 
1001
1083
  /**
1002
- * Single-bundle variant for chart families that have one tooltip execution per
1003
- * chart (e.g. Highcharts). Returns `undefined` while no bundle is provided or
1004
- * before the first result lands; consumers handle that as "no external values".
1084
+ * Single-execution variant for chart families that have one tooltip execution
1085
+ * per chart (e.g. Highcharts). Returns `undefined` while no execution is
1086
+ * provided or before the first result lands; consumers handle that as "no
1087
+ * external values".
1005
1088
  *
1006
1089
  * @internal
1007
1090
  */
1008
- export declare function useTooltipLookup(bundle: ITooltipExecutionBundle | undefined, separators?: ISeparators, localizedStrings?: ITooltipLookupLocalizedStrings): Map<string, IResolvedReferenceValues> | undefined;
1091
+ export declare function useTooltipLookup(execution: ITooltipExecution | undefined, separators?: ISeparators): Map<string, IResolvedReferenceValues> | undefined;
1009
1092
 
1010
1093
  /**
1011
- * Multi-bundle variant for chart families that key tooltip executions per
1012
- * sub-target (e.g. geo per-layer). `context` is required so the produced
1013
- * lookup carries whatever the caller needs to interpret the result —
1014
- * downstream code does not have to defensively check for missing context.
1094
+ * Multi-execution variant for chart families that key tooltip executions per
1095
+ * sub-target (e.g. geo per-layer). Each entry runs through the same batch →
1096
+ * per-reference fan-out as the single-execution variant. `context` travels with
1097
+ * the built lookup so downstream code needn't defensively check for it.
1015
1098
  *
1016
1099
  * @internal
1017
1100
  */
1018
- export declare function useTooltipLookupExecutions<TContext>(entries: readonly ITooltipLookupExecutionEntry<TContext>[], separators?: ISeparators, localizedStrings?: ITooltipLookupLocalizedStrings): Map<string, ITooltipLookupExecutionResult<TContext>>;
1101
+ export declare function useTooltipLookupExecutions<TContext>(entries: readonly ITooltipLookupExecutionEntry<TContext>[], separators?: ISeparators): Map<string, ITooltipLookupExecutionResult<TContext>>;
1019
1102
 
1020
1103
  /**
1021
1104
  * Returns the value if it is non-empty or a fallback text.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gooddata/sdk-ui-vis-commons",
3
- "version": "11.41.0-alpha.0",
3
+ "version": "11.41.0-alpha.2",
4
4
  "description": "GoodData.UI SDK - common functionality for different types of visualizations",
5
5
  "license": "MIT",
6
6
  "author": "GoodData Corporation",
@@ -36,11 +36,11 @@
36
36
  "react-intl": "7.1.11",
37
37
  "react-measure": "^2.5.2",
38
38
  "tslib": "2.8.1",
39
- "@gooddata/sdk-backend-spi": "11.41.0-alpha.0",
40
- "@gooddata/sdk-model": "11.41.0-alpha.0",
41
- "@gooddata/sdk-ui": "11.41.0-alpha.0",
42
- "@gooddata/sdk-ui-kit": "11.41.0-alpha.0",
43
- "@gooddata/sdk-ui-theme-provider": "11.41.0-alpha.0"
39
+ "@gooddata/sdk-backend-spi": "11.41.0-alpha.2",
40
+ "@gooddata/sdk-ui": "11.41.0-alpha.2",
41
+ "@gooddata/sdk-ui-kit": "11.41.0-alpha.2",
42
+ "@gooddata/sdk-ui-theme-provider": "11.41.0-alpha.2",
43
+ "@gooddata/sdk-model": "11.41.0-alpha.2"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@microsoft/api-documenter": "^7.17.0",
@@ -81,11 +81,11 @@
81
81
  "typescript": "5.9.3",
82
82
  "vitest": "4.1.8",
83
83
  "vitest-dom": "0.1.1",
84
- "@gooddata/eslint-config": "11.41.0-alpha.0",
85
- "@gooddata/oxlint-config": "11.41.0-alpha.0",
86
- "@gooddata/reference-workspace": "11.41.0-alpha.0",
87
- "@gooddata/sdk-backend-mockingbird": "11.41.0-alpha.0",
88
- "@gooddata/stylelint-config": "11.41.0-alpha.0"
84
+ "@gooddata/eslint-config": "11.41.0-alpha.2",
85
+ "@gooddata/oxlint-config": "11.41.0-alpha.2",
86
+ "@gooddata/reference-workspace": "11.41.0-alpha.2",
87
+ "@gooddata/sdk-backend-mockingbird": "11.41.0-alpha.2",
88
+ "@gooddata/stylelint-config": "11.41.0-alpha.2"
89
89
  },
90
90
  "peerDependencies": {
91
91
  "react": "^18.0.0 || ^19.0.0",