diff --git a/src/legends/swatches.js b/src/legends/swatches.js index f49a0b84de..c1b54e655f 100644 --- a/src/legends/swatches.js +++ b/src/legends/swatches.js @@ -13,6 +13,12 @@ function maybeScale(scale, key) { return s; } +function checkDomainMatch(scale, symbol) { + for (const d of symbol.domain) { + if (scale.scale(d) === undefined) throw new Error("the color and symbol scale domains must match"); + } +} + export function legendSwatches(color, {opacity, ...options} = {}) { if (!isOrdinalScale(color) && !isThresholdScale(color)) throw new Error(`swatches legend requires ordinal or threshold color scale (not ${color.type})`); @@ -49,6 +55,8 @@ export function legendSymbols( const [vs, cs] = maybeColorChannel(stroke); const sf = maybeScale(scale, vf); const ss = maybeScale(scale, vs); + if (vf === "color") checkDomainMatch(sf, symbol); + if (vs === "color") checkDomainMatch(ss, symbol); const size = r * r * Math.PI; fillOpacity = maybeNumberChannel(fillOpacity)[1]; strokeOpacity = maybeNumberChannel(strokeOpacity)[1]; diff --git a/test/legend-test.js b/test/legend-test.js index ed99cf8695..1bf8672abd 100644 --- a/test/legend-test.js +++ b/test/legend-test.js @@ -22,3 +22,26 @@ it("Plot.legend({}) throws an error", () => { it("Plot.legend({color: {}}) throws an error", () => { assert.throws(() => Plot.legend({color: {}}), /unknown legend type/); }); + +it("Plot.legend({color, symbol}) throws an error when the color and symbol domains don't match", () => { + assert.throws( + () => Plot.legend({color: {domain: [1, 2, 3]}, symbol: {domain: [4, 5, 6]}}), + /the color and symbol scale domains must match/ + ); +}); + +it("Plot.legend({color, symbol}) applies the color scale to the symbol stroke when the domains match", () => { + const legend = Plot.legend({color: {domain: [1, 2, 3]}, symbol: {domain: [1, 2, 3]}}); + const strokes = [...legend.querySelectorAll("svg")].map((svg) => svg.getAttribute("stroke")); + assert.deepStrictEqual(strokes, ["#4269d0", "#efb118", "#ff725c"]); +}); + +it("Plot.legend({color, symbol}) applies the color scale to the symbol stroke when the color domain is a superset", () => { + const legend = Plot.legend({color: {domain: [1, 2, 3, 4]}, symbol: {domain: [1, 2, 3]}}); + const strokes = [...legend.querySelectorAll("svg")].map((svg) => svg.getAttribute("stroke")); + assert.deepStrictEqual(strokes, ["#4269d0", "#efb118", "#ff725c"]); +}); + +it("Plot.legend({symbol}) does not throw when there is no color scale", () => { + assert.doesNotThrow(() => Plot.legend({symbol: {domain: [1, 2, 3]}})); +});