@gooddata/sdk-ui-vis-commons 11.42.0-alpha.0 → 11.42.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,3 +1,4 @@
1
+ export declare const URL_PATTERN = "(?:[^()\\s]|\\([^)\\n]*\\))+";
1
2
  /**
2
3
  * @internal
3
4
  */
@@ -1 +1 @@
1
- {"version":3,"file":"markdownToHtml.d.ts","sourceRoot":"","sources":["../../src/customTooltip/markdownToHtml.ts"],"names":[],"mappings":"AAoIA;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAuBvD"}
1
+ {"version":3,"file":"markdownToHtml.d.ts","sourceRoot":"","sources":["../../src/customTooltip/markdownToHtml.ts"],"names":[],"mappings":"AA8CA,eAAO,MAAM,WAAW,iCAAiC,CAAC;AA2F1D;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAuBvD"}
@@ -37,10 +37,13 @@ function isSafeLinkUrl(url) {
37
37
  return /^https?:\/\//i.test(url);
38
38
  }
39
39
  // URL pattern allowing one level of balanced parens, e.g.
40
- // https://en.wikipedia.org/wiki/Page_(name)
41
- const URL_PATTERN = "(?:[^()\\s]|\\([^)]*\\))+";
42
- const IMAGE_REGEX = new RegExp(`!\\[([^\\]]*)\\]\\((${URL_PATTERN})\\)`, "g");
43
- const LINK_REGEX = new RegExp(`\\[([^\\]]+)\\]\\((${URL_PATTERN})\\)`, "g");
40
+ // https://en.wikipedia.org/wiki/Page_(name). Shared with `referenceResolver`
41
+ // so blanking applies exactly where this renderer sees an image/link.
42
+ export const URL_PATTERN = "(?:[^()\\s]|\\([^)\\n]*\\))+";
43
+ // URL is optional: a data reference that resolved to no data is blanked upstream
44
+ // (see `referenceResolver`), leaving `![alt]()` / `[text]()` to render here.
45
+ const IMAGE_REGEX = new RegExp(`!\\[([^\\]]*)\\]\\((${URL_PATTERN})?\\)`, "g");
46
+ const LINK_REGEX = new RegExp(`\\[([^\\]]+)\\]\\((${URL_PATTERN})?\\)`, "g");
44
47
  // Italic content must have non-whitespace at both inner boundaries. This keeps
45
48
  // arithmetic-style text (e.g. `5 * 3 * 2`) from being misread as italics.
46
49
  const ITALIC_ASTERISK_REGEX = /\*([^\s*][^*]*[^\s*]|[^\s*])\*/g;
@@ -48,19 +51,21 @@ const ITALIC_UNDERSCORE_REGEX = /(?<!\w)_([^\s_][^_]*[^\s_]|[^\s_])_(?!\w)/g;
48
51
  function processInlineMarkdown(text) {
49
52
  let result = escapeHtml(text);
50
53
  // Inline style as fallback since the tooltip renders outside the normal DOM tree.
51
- result = result.replace(IMAGE_REGEX, (_match, alt, url) => {
52
- if (!isSafeImageUrl(url)) {
54
+ result = result.replace(IMAGE_REGEX, (_match, alt, url = "") => {
55
+ if (url && !isSafeImageUrl(url)) {
53
56
  return `${alt}`;
54
57
  }
58
+ // Empty URL → broken image keeping the alt (RichText parity).
55
59
  return `<img src="${url}" alt="${alt}" style="max-width: 100%; display: block; margin: 4px 0;" />`;
56
60
  });
57
61
  // Always emit a `target="_blank"` anchor for http(s) URLs; whether the user
58
62
  // can practically reach it depends on the tooltip mode's lifecycle (the
59
63
  // tooltip needs to stay open long enough to mouse over the link).
60
- result = result.replace(LINK_REGEX, (_match, linkText, url) => {
61
- if (!isSafeLinkUrl(url)) {
64
+ result = result.replace(LINK_REGEX, (_match, linkText, url = "") => {
65
+ if (url && !isSafeLinkUrl(url)) {
62
66
  return linkText;
63
67
  }
68
+ // Empty URL → anchor with empty href, like RichText (points at the current page).
64
69
  return `<a href="${url}" target="_blank" rel="noopener noreferrer">${linkText}</a>`;
65
70
  });
66
71
  // Bold-italic: ***text*** — must run before bold and italic so the triple
@@ -1 +1 @@
1
- {"version":3,"file":"referenceResolver.d.ts","sourceRoot":"","sources":["../../src/customTooltip/referenceResolver.ts"],"names":[],"mappings":"AAIA,OAAO,EACH,KAAK,wBAAwB,EAC7B,KAAK,wBAAwB,EAIhC,MAAM,YAAY,CAAC;AAgCpB;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,CAC7B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,wBAAwB,EAChC,OAAO,EAAE,wBAAwB,GAClC,MAAM,CAYR"}
1
+ {"version":3,"file":"referenceResolver.d.ts","sourceRoot":"","sources":["../../src/customTooltip/referenceResolver.ts"],"names":[],"mappings":"AAKA,OAAO,EACH,KAAK,wBAAwB,EAC7B,KAAK,wBAAwB,EAIhC,MAAM,YAAY,CAAC;AAuEpB;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,CAC7B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,wBAAwB,EAChC,OAAO,EAAE,wBAAwB,GAClC,MAAM,CAWR"}
@@ -1,5 +1,6 @@
1
1
  // (C) 2026 GoodData Corporation
2
2
  import { REFERENCE_REGEX_MATCH } from "@gooddata/sdk-ui-kit";
3
+ import { URL_PATTERN } from "./markdownToHtml.js";
3
4
  import { labelKey, metricKey, } from "./types.js";
4
5
  // Markdown metacharacters that, if present in a substituted value, would be
5
6
  // reinterpreted as formatting by `markdownToHtml`. Backslash-escape them so the
@@ -28,6 +29,26 @@ function renderReference(ref, strings) {
28
29
  return strings.noFetch;
29
30
  }
30
31
  }
32
+ // Prefix is matched case-insensitively (`metric` | `label`); LDM identifiers are case-sensitive.
33
+ function lookupRef(values, prefix, identifier) {
34
+ return values[prefix.toLowerCase() === "metric" ? metricKey(identifier) : labelKey(identifier)];
35
+ }
36
+ // In image/link URL targets a no-data reference must collapse to empty rather than
37
+ // the "(No data)" placeholder, which would break markdown parsing and leak the raw
38
+ // `![alt](...)` syntax. Blanking yields a broken image / empty-href link, matching
39
+ // RichText. Image alt is blanked too; link text keeps the normal placeholder.
40
+ // Slot grammar mirrors `markdownToHtml` (shared URL_PATTERN, single-line slots),
41
+ // so blanking applies only where the renderer will actually see an image/link.
42
+ const IMAGE_CONSTRUCT_REGEX = new RegExp(`!\\[([^\\]\\n]*)\\]\\((${URL_PATTERN})?\\)`, "g");
43
+ const LINK_CONSTRUCT_REGEX = new RegExp(`(?<!!)\\[([^\\]\\n]+)\\]\\((${URL_PATTERN})?\\)`, "g");
44
+ function blankNonValueRefs(slot, values) {
45
+ return slot.replace(REFERENCE_REGEX_MATCH, (fullMatch, _wrapped, _key, prefix, identifier) => lookupRef(values, prefix, identifier)?.kind === "value" ? fullMatch : "");
46
+ }
47
+ function blankNonValueTargets(content, values) {
48
+ return content
49
+ .replace(IMAGE_CONSTRUCT_REGEX, (_match, alt, url = "") => `![${blankNonValueRefs(alt, values)}](${blankNonValueRefs(url, values)})`)
50
+ .replace(LINK_CONSTRUCT_REGEX, (_match, text, url = "") => `[${text}](${blankNonValueRefs(url, values)})`);
51
+ }
31
52
  /**
32
53
  * Substitutes `{metric/id}` and `{label/id}` references in markdown content
33
54
  * with resolved values from the lookup table.
@@ -49,11 +70,6 @@ export function resolveReferences(content, values, strings) {
49
70
  if (!content) {
50
71
  return "";
51
72
  }
52
- return content.replace(REFERENCE_REGEX_MATCH, (_fullMatch, _wrapped, _key, prefix, identifier) => {
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));
58
- });
73
+ // Blank no-data image/link targets first, before they reach substitution.
74
+ return blankNonValueTargets(content, values).replace(REFERENCE_REGEX_MATCH, (_fullMatch, _wrapped, _key, prefix, identifier) => escapeMarkdownMetachars(renderReference(lookupRef(values, prefix, identifier), strings)));
59
75
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gooddata/sdk-ui-vis-commons",
3
- "version": "11.42.0-alpha.0",
3
+ "version": "11.42.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.42.0-alpha.0",
40
- "@gooddata/sdk-model": "11.42.0-alpha.0",
41
- "@gooddata/sdk-ui": "11.42.0-alpha.0",
42
- "@gooddata/sdk-ui-theme-provider": "11.42.0-alpha.0",
43
- "@gooddata/sdk-ui-kit": "11.42.0-alpha.0"
39
+ "@gooddata/sdk-backend-spi": "11.42.0-alpha.2",
40
+ "@gooddata/sdk-model": "11.42.0-alpha.2",
41
+ "@gooddata/sdk-ui": "11.42.0-alpha.2",
42
+ "@gooddata/sdk-ui-kit": "11.42.0-alpha.2",
43
+ "@gooddata/sdk-ui-theme-provider": "11.42.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.42.0-alpha.0",
85
- "@gooddata/oxlint-config": "11.42.0-alpha.0",
86
- "@gooddata/sdk-backend-mockingbird": "11.42.0-alpha.0",
87
- "@gooddata/reference-workspace": "11.42.0-alpha.0",
88
- "@gooddata/stylelint-config": "11.42.0-alpha.0"
84
+ "@gooddata/eslint-config": "11.42.0-alpha.2",
85
+ "@gooddata/oxlint-config": "11.42.0-alpha.2",
86
+ "@gooddata/sdk-backend-mockingbird": "11.42.0-alpha.2",
87
+ "@gooddata/reference-workspace": "11.42.0-alpha.2",
88
+ "@gooddata/stylelint-config": "11.42.0-alpha.2"
89
89
  },
90
90
  "peerDependencies": {
91
91
  "react": "^18.0.0 || ^19.0.0",