@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.
- package/esm/customTooltip/composeSectionHtml.d.ts +2 -2
- package/esm/customTooltip/composeSectionHtml.js +2 -2
- package/esm/customTooltip/localizedStrings.d.ts +11 -0
- package/esm/customTooltip/localizedStrings.js +22 -0
- package/esm/customTooltip/referenceResolver.d.ts +5 -5
- package/esm/customTooltip/referenceResolver.js +28 -9
- package/esm/customTooltip/referenceStatus.d.ts +22 -0
- package/esm/customTooltip/referenceStatus.js +33 -0
- package/esm/customTooltip/tooltipExecution.d.ts +18 -1
- package/esm/customTooltip/tooltipExecution.js +41 -18
- package/esm/customTooltip/tooltipLookup.d.ts +3 -11
- package/esm/customTooltip/tooltipLookup.js +8 -25
- package/esm/customTooltip/types.d.ts +48 -2
- package/esm/customTooltip/types.js +16 -1
- package/esm/customTooltip/useTooltipLookupExecutions.d.ts +20 -21
- package/esm/customTooltip/useTooltipLookupExecutions.js +73 -29
- package/esm/index.d.ts +4 -3
- package/esm/index.js +3 -1
- package/esm/sdk-ui-vis-commons.d.ts +117 -34
- package/package.json +11 -11
|
@@ -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,
|
|
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,
|
|
12
|
+
export function composeCustomTooltipSectionHtml(content, inChartValues, externalValues, localizedStrings) {
|
|
13
13
|
const merged = { ...externalValues, ...inChartValues };
|
|
14
|
-
const resolved = resolveReferences(content, merged,
|
|
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
|
|
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
|
|
15
|
-
*
|
|
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,
|
|
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
|
|
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
|
|
25
|
-
*
|
|
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,
|
|
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
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
const
|
|
38
|
-
return escapeMarkdownMetachars(
|
|
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):
|
|
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
|
|
71
|
-
* Labels get a max+count pair (mirrors the RichText
|
|
72
|
-
* lookup can render "(Multiple items)" when a label
|
|
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
|
|
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"
|
|
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
|
|
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
|
-
*
|
|
123
|
-
*
|
|
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
|
-
|
|
128
|
-
const
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
55
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
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]:
|
|
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
|
-
|
|
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
|
|
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
|
-
*
|
|
8
|
-
*
|
|
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:
|
|
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
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
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
|
|
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
|
-
|
|
11
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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-
|
|
20
|
-
* chart (e.g. Highcharts). Returns `undefined` while no
|
|
21
|
-
* before the first result lands; consumers handle that as "no
|
|
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(
|
|
26
|
-
const fingerprint =
|
|
59
|
+
export function useTooltipLookup(execution, separators) {
|
|
60
|
+
const fingerprint = execution?.batch.execution.fingerprint();
|
|
27
61
|
const { result } = useCancelablePromise({
|
|
28
|
-
promise:
|
|
62
|
+
promise: execution ? () => runWithFallback(execution) : undefined,
|
|
29
63
|
}, [fingerprint]);
|
|
30
|
-
return useMemo(() =>
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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-
|
|
39
|
-
* sub-target (e.g. geo per-layer).
|
|
40
|
-
*
|
|
41
|
-
*
|
|
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
|
|
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
|
-
|
|
57
|
-
|
|
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
|
|
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
|
-
*
|
|
60
|
-
* (
|
|
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
|
|
67
|
+
export declare function buildTooltipExecution(executionFactory: IExecutionFactory, chartDefinition: IExecutionDefinition, tooltipContent: string, options?: IBuildTooltipExecutionOptions): ITooltipExecution | null;
|
|
65
68
|
|
|
66
69
|
/**
|
|
67
|
-
*
|
|
68
|
-
*
|
|
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
|
|
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,
|
|
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
|
|
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]:
|
|
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
|
-
*
|
|
814
|
-
*
|
|
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:
|
|
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
|
-
*
|
|
837
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
974
|
-
*
|
|
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,
|
|
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-
|
|
1003
|
-
* chart (e.g. Highcharts). Returns `undefined` while no
|
|
1004
|
-
* before the first result lands; consumers handle that as "no
|
|
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(
|
|
1091
|
+
export declare function useTooltipLookup(execution: ITooltipExecution | undefined, separators?: ISeparators): Map<string, IResolvedReferenceValues> | undefined;
|
|
1009
1092
|
|
|
1010
1093
|
/**
|
|
1011
|
-
* Multi-
|
|
1012
|
-
* sub-target (e.g. geo per-layer).
|
|
1013
|
-
*
|
|
1014
|
-
*
|
|
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
|
|
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.
|
|
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.
|
|
40
|
-
"@gooddata/sdk-
|
|
41
|
-
"@gooddata/sdk-ui": "11.41.0-alpha.
|
|
42
|
-
"@gooddata/sdk-ui-
|
|
43
|
-
"@gooddata/sdk-
|
|
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.
|
|
85
|
-
"@gooddata/oxlint-config": "11.41.0-alpha.
|
|
86
|
-
"@gooddata/reference-workspace": "11.41.0-alpha.
|
|
87
|
-
"@gooddata/sdk-backend-mockingbird": "11.41.0-alpha.
|
|
88
|
-
"@gooddata/stylelint-config": "11.41.0-alpha.
|
|
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",
|