diff --git a/src/marks/tip.js b/src/marks/tip.js
index fcb50a1056..10ce7f8973 100644
--- a/src/marks/tip.js
+++ b/src/marks/tip.js
@@ -45,6 +45,7 @@ export class Tip extends Mark {
textAnchor = "start",
textOverflow,
textPadding = 8,
+ title,
pointerSize = 12,
pathFilter = "drop-shadow(0 3px 4px rgba(0,0,0,0.2))"
} = options;
@@ -56,7 +57,8 @@ export class Tip extends Mark {
x1: {value: x1, scale: "x", optional: x2 == null},
y1: {value: y1, scale: "y", optional: y2 == null},
x2: {value: x2, scale: "x", optional: x1 == null},
- y2: {value: y2, scale: "y", optional: y1 == null}
+ y2: {value: y2, scale: "y", optional: y1 == null},
+ title: {value: title, optional: true} // filter: defined
},
options,
defaults
diff --git a/src/style.js b/src/style.js
index 38c55c6968..b3e046cc91 100644
--- a/src/style.js
+++ b/src/style.js
@@ -143,9 +143,9 @@ export function styles(
mark.shapeRendering = impliedString(shapeRendering, "auto");
return {
- title: {value: title, optional: true},
- href: {value: href, optional: true},
- ariaLabel: {value: variaLabel, optional: true},
+ title: {value: title, optional: true, filter: null},
+ href: {value: href, optional: true, filter: null},
+ ariaLabel: {value: variaLabel, optional: true, filter: null},
fill: {value: vfill, scale: "auto", optional: true},
fillOpacity: {value: vfillOpacity, scale: "auto", optional: true},
stroke: {value: vstroke, scale: "auto", optional: true},
diff --git a/test/output/sparseTitle.svg b/test/output/sparseTitle.svg
new file mode 100644
index 0000000000..0c345365f6
--- /dev/null
+++ b/test/output/sparseTitle.svg
@@ -0,0 +1,10987 @@
+
\ No newline at end of file
diff --git a/test/output/sparseTitleTip.svg b/test/output/sparseTitleTip.svg
new file mode 100644
index 0000000000..2f99dd9a30
--- /dev/null
+++ b/test/output/sparseTitleTip.svg
@@ -0,0 +1,10991 @@
+
\ No newline at end of file
diff --git a/test/plots/index.ts b/test/plots/index.ts
index 93bc831398..b0544b26aa 100644
--- a/test/plots/index.ts
+++ b/test/plots/index.ts
@@ -272,6 +272,7 @@ export * from "./single-value-bar.js";
export * from "./single-value-bin.js";
export * from "./software-versions.js";
export * from "./sparse-cell.js";
+export * from "./sparse-title.js";
export * from "./stacked-bar.js";
export * from "./stacked-rect.js";
export * from "./stargazers-binned.js";
diff --git a/test/plots/sparse-title.ts b/test/plots/sparse-title.ts
new file mode 100644
index 0000000000..69cb2fdec8
--- /dev/null
+++ b/test/plots/sparse-title.ts
@@ -0,0 +1,46 @@
+import * as Plot from "@observablehq/plot";
+import * as d3 from "d3";
+
+export async function sparseTitle() {
+ const olympians = await d3.csv("data/athletes.csv", d3.autoType);
+ return Plot.plot({
+ grid: true,
+ marks: [
+ Plot.dot(olympians, {
+ x: "weight",
+ y: "height",
+ fy: "sex",
+ sort: (d) => !!d.info,
+ fill: (d) => (d.info ? "currentColor" : "#aaa"),
+ stroke: "white",
+ strokeWidth: 0.25,
+ title: (d) => (d.info ? [(d.name, d.info)].join("\n\n") : undefined)
+ })
+ ]
+ });
+}
+
+export async function sparseTitleTip() {
+ const olympians = await d3.csv("data/athletes.csv", d3.autoType);
+ return Plot.plot({
+ grid: true,
+ marks: [
+ Plot.dot(olympians, {
+ x: "weight",
+ y: "height",
+ fy: "sex",
+ sort: (d) => !!d.info,
+ stroke: (d) => (d.info ? "currentColor" : "#aaa")
+ }),
+ Plot.tip(
+ olympians,
+ Plot.pointer({
+ x: "weight",
+ y: "height",
+ fy: "sex",
+ title: (d) => (d.info ? [(d.name, d.info)].join("\n\n") : undefined)
+ })
+ )
+ ]
+ });
+}