@vitessce/statistical-plots 3.5.8 → 3.5.9

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,5 +1,5 @@
1
1
  import { i as inflate_1 } from "./pako.esm-68f84e2a.js";
2
- import { B as BaseDecoder } from "./index-0f4fe21d.js";
2
+ import { B as BaseDecoder } from "./index-dc733355.js";
3
3
  import "react";
4
4
  import "@vitessce/vit-s";
5
5
  import "react-dom";
@@ -2569,6 +2569,9 @@ const DEFAULT_LIGHT2_COLOR = [235, 235, 235];
2569
2569
  function getDefaultColor(theme) {
2570
2570
  return theme === "dark" ? DEFAULT_DARK_COLOR : theme === "light" ? DEFAULT_LIGHT_COLOR$3 : DEFAULT_LIGHT2_COLOR;
2571
2571
  }
2572
+ function getDefaultForegroundColor(theme) {
2573
+ return theme === "dark" ? DEFAULT_LIGHT2_COLOR : DEFAULT_DARK_COLOR;
2574
+ }
2572
2575
  const PALETTE = [
2573
2576
  [68, 119, 170],
2574
2577
  [136, 204, 238],
@@ -133508,16 +133511,16 @@ function addDecoder(cases2, importFn) {
133508
133511
  }
133509
133512
  cases2.forEach((c2) => registry$1.set(c2, importFn));
133510
133513
  }
133511
- addDecoder([void 0, 1], () => import("./raw-d8d7ab7f.js").then((m2) => m2.default));
133512
- addDecoder(5, () => import("./lzw-70f852cc.js").then((m2) => m2.default));
133514
+ addDecoder([void 0, 1], () => import("./raw-f7587aff.js").then((m2) => m2.default));
133515
+ addDecoder(5, () => import("./lzw-9572eac3.js").then((m2) => m2.default));
133513
133516
  addDecoder(6, () => {
133514
133517
  throw new Error("old style JPEG compression is not supported.");
133515
133518
  });
133516
- addDecoder(7, () => import("./jpeg-280f0ee1.js").then((m2) => m2.default));
133517
- addDecoder([8, 32946], () => import("./deflate-1679ef33.js").then((m2) => m2.default));
133518
- addDecoder(32773, () => import("./packbits-393c67b2.js").then((m2) => m2.default));
133519
- addDecoder(34887, () => import("./lerc-12264a36.js").then((m2) => m2.default));
133520
- addDecoder(50001, () => import("./webimage-5d24a8e2.js").then((m2) => m2.default));
133519
+ addDecoder(7, () => import("./jpeg-a83077be.js").then((m2) => m2.default));
133520
+ addDecoder([8, 32946], () => import("./deflate-19841f78.js").then((m2) => m2.default));
133521
+ addDecoder(32773, () => import("./packbits-cce11fbc.js").then((m2) => m2.default));
133522
+ addDecoder(34887, () => import("./lerc-1edd075a.js").then((m2) => m2.default));
133523
+ addDecoder(50001, () => import("./webimage-8d38cd8b.js").then((m2) => m2.default));
133521
133524
  function decodeRowAcc(row, stride) {
133522
133525
  let length2 = row.length - stride;
133523
133526
  let offset5 = 0;
@@ -207114,7 +207117,8 @@ function Treemap(props) {
207114
207117
  marginTop = 5,
207115
207118
  marginRight = 5,
207116
207119
  marginLeft = 80,
207117
- marginBottom
207120
+ marginBottom,
207121
+ onNodeClick
207118
207122
  } = props;
207119
207123
  const hierarchyData = useMemo(() => {
207120
207124
  if (!obsCounts) {
@@ -207179,7 +207183,11 @@ function Treemap(props) {
207179
207183
  leaf.append("rect").attr("id", (d) => {
207180
207184
  d.leafUid = getLeafUid();
207181
207185
  return d.leafUid.id;
207182
- }).attr("fill", (d) => colorScale2(getPathForColoring(d))).attr("fill-opacity", 0.8).attr("width", (d) => d.x1 - d.x0).attr("height", (d) => d.y1 - d.y0);
207186
+ }).attr("fill", (d) => colorScale2(getPathForColoring(d))).attr("fill-opacity", 0.8).attr("width", (d) => d.x1 - d.x0).attr("height", (d) => d.y1 - d.y0).on("click", (e3, d) => {
207187
+ var _a2, _b, _c;
207188
+ const obsSetPath = hierarchyLevels[0] === "obsSet" ? (_b = (_a2 = d.parent) == null ? void 0 : _a2.data) == null ? void 0 : _b[0] : (_c = d.data) == null ? void 0 : _c[0];
207189
+ onNodeClick(obsSetPath);
207190
+ });
207183
207191
  leaf.append("clipPath").attr("id", (d) => {
207184
207192
  d.clipUid = getClipUid();
207185
207193
  return d.clipUid.id;
@@ -207211,7 +207219,8 @@ function Treemap(props) {
207211
207219
  obsSetColorScale,
207212
207220
  sampleSetColorScale,
207213
207221
  obsColorEncoding,
207214
- hierarchyLevels
207222
+ hierarchyLevels,
207223
+ onNodeClick
207215
207224
  ]);
207216
207225
  return /* @__PURE__ */ jsxRuntimeExports.jsx(
207217
207226
  "svg",
@@ -207487,6 +207496,9 @@ function TreemapSubscriber(props) {
207487
207496
  sampleSetSelection
207488
207497
  // TODO: consider filtering-related coordination values
207489
207498
  ]);
207499
+ const onNodeClick = useCallback((obsSetPath) => {
207500
+ setObsSetSelection([obsSetPath]);
207501
+ }, [setObsSetSelection]);
207490
207502
  return /* @__PURE__ */ jsxRuntimeExports.jsx(
207491
207503
  TitleInfo,
207492
207504
  {
@@ -207523,7 +207535,8 @@ function TreemapSubscriber(props) {
207523
207535
  obsSetColor,
207524
207536
  sampleSetColor,
207525
207537
  obsSetSelection,
207526
- sampleSetSelection
207538
+ sampleSetSelection,
207539
+ onNodeClick
207527
207540
  }
207528
207541
  ) })
207529
207542
  }
@@ -207596,7 +207609,9 @@ function VolcanoPlot(props) {
207596
207609
  svg.append("g").attr("transform", `translate(0,${height2 - marginBottom})`).call(axisBottom(xScale));
207597
207610
  svg.append("g").attr("transform", `translate(${marginLeft},0)`).call(axisLeft(yScale));
207598
207611
  const titleG = svg.append("g");
207599
- const fgColor = "black";
207612
+ const fgColor = colorArrayToString(
207613
+ getDefaultForegroundColor(theme)
207614
+ );
207600
207615
  titleG.append("text").attr("text-anchor", "middle").attr("x", -innerHeight / 2).attr("y", 15).attr("transform", "rotate(-90)").text("-log10 p-value").style("font-size", "12px").style("fill", fgColor);
207601
207616
  titleG.append("text").attr("text-anchor", "middle").attr("x", marginLeft + innerWidth / 2).attr("y", height2 - 10).text("log2 fold-change").style("font-size", "12px").style("fill", fgColor);
207602
207617
  const obsSetsColumnNameMappingReversed = Object.fromEntries(
@@ -208369,7 +208384,7 @@ function FeatureSetEnrichmentBarPlot(props) {
208369
208384
  x: {
208370
208385
  field: "minusLog10p",
208371
208386
  type: "quantitative",
208372
- title: "- log10 p-value"
208387
+ title: "-log10 p-value"
208373
208388
  },
208374
208389
  color: {
208375
208390
  field: "key",
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { b, e, C, d, a, h, D, f, E, F, c, T, V } from "./index-0f4fe21d.js";
1
+ import { b, e, C, d, a, h, D, f, E, F, c, T, V } from "./index-dc733355.js";
2
2
  import "react";
3
3
  import "@vitessce/vit-s";
4
4
  import "react-dom";
@@ -1,4 +1,4 @@
1
- import { B as BaseDecoder } from "./index-0f4fe21d.js";
1
+ import { B as BaseDecoder } from "./index-dc733355.js";
2
2
  import "react";
3
3
  import "@vitessce/vit-s";
4
4
  import "react-dom";
@@ -1,5 +1,5 @@
1
1
  import { i as inflate_1 } from "./pako.esm-68f84e2a.js";
2
- import { g as getDefaultExportFromCjs, B as BaseDecoder } from "./index-0f4fe21d.js";
2
+ import { g as getDefaultExportFromCjs, B as BaseDecoder } from "./index-dc733355.js";
3
3
  import "react";
4
4
  import "@vitessce/vit-s";
5
5
  import "react-dom";
@@ -1,4 +1,4 @@
1
- import { B as BaseDecoder } from "./index-0f4fe21d.js";
1
+ import { B as BaseDecoder } from "./index-dc733355.js";
2
2
  import "react";
3
3
  import "@vitessce/vit-s";
4
4
  import "react-dom";
@@ -1,4 +1,4 @@
1
- import { B as BaseDecoder } from "./index-0f4fe21d.js";
1
+ import { B as BaseDecoder } from "./index-dc733355.js";
2
2
  import "react";
3
3
  import "@vitessce/vit-s";
4
4
  import "react-dom";
@@ -1,4 +1,4 @@
1
- import { B as BaseDecoder } from "./index-0f4fe21d.js";
1
+ import { B as BaseDecoder } from "./index-dc733355.js";
2
2
  import "react";
3
3
  import "@vitessce/vit-s";
4
4
  import "react-dom";
@@ -1,4 +1,4 @@
1
- import { B as BaseDecoder } from "./index-0f4fe21d.js";
1
+ import { B as BaseDecoder } from "./index-dc733355.js";
2
2
  import "react";
3
3
  import "@vitessce/vit-s";
4
4
  import "react-dom";
@@ -113,7 +113,7 @@ export default function FeatureSetEnrichmentBarPlot(props) {
113
113
  x: {
114
114
  field: 'minusLog10p',
115
115
  type: 'quantitative',
116
- title: '- log10 p-value',
116
+ title: '-log10 p-value',
117
117
  },
118
118
  color: {
119
119
  field: 'key',
@@ -1 +1 @@
1
- {"version":3,"file":"Treemap.d.ts","sourceRoot":"","sources":["../src/Treemap.js"],"names":[],"mappings":"AAsBA;;;;;;;;GAQG;AACH,yDA0KC"}
1
+ {"version":3,"file":"Treemap.d.ts","sourceRoot":"","sources":["../src/Treemap.js"],"names":[],"mappings":"AAsBA;;;;;;;;GAQG;AACH,yDAkLC"}
@@ -29,7 +29,7 @@ function uidGenerator(prefix) {
29
29
  * @returns
30
30
  */
31
31
  export default function Treemap(props) {
32
- const { obsCounts, obsColorEncoding, hierarchyLevels, theme, width, height, obsType, sampleType, obsSetColor, sampleSetColor, obsSetSelection, sampleSetSelection, marginTop = 5, marginRight = 5, marginLeft = 80, marginBottom, } = props;
32
+ const { obsCounts, obsColorEncoding, hierarchyLevels, theme, width, height, obsType, sampleType, obsSetColor, sampleSetColor, obsSetSelection, sampleSetSelection, marginTop = 5, marginRight = 5, marginLeft = 80, marginBottom, onNodeClick, } = props;
33
33
  const hierarchyData = useMemo(() => {
34
34
  // Support both sampleSet->obsSet and
35
35
  // obsSet->sampleSet hierarchy modes
@@ -114,7 +114,13 @@ export default function Treemap(props) {
114
114
  .attr('fill', d => colorScale(getPathForColoring(d)))
115
115
  .attr('fill-opacity', 0.8)
116
116
  .attr('width', d => d.x1 - d.x0)
117
- .attr('height', d => d.y1 - d.y0);
117
+ .attr('height', d => d.y1 - d.y0)
118
+ .on('click', (e, d) => {
119
+ const obsSetPath = (hierarchyLevels[0] === 'obsSet'
120
+ ? d.parent?.data?.[0]
121
+ : d.data?.[0]);
122
+ onNodeClick(obsSetPath);
123
+ });
118
124
  // Append a clipPath to ensure text does not overflow.
119
125
  leaf.append('clipPath')
120
126
  .attr('id', (d) => {
@@ -142,7 +148,7 @@ export default function Treemap(props) {
142
148
  }, [width, height, marginLeft, marginBottom, theme, marginTop, marginRight,
143
149
  obsType, sampleType, treemapLeaves, sampleSetColor, sampleSetSelection,
144
150
  obsSetSelection, obsSetColor, obsSetColorScale, sampleSetColorScale,
145
- obsColorEncoding, hierarchyLevels,
151
+ obsColorEncoding, hierarchyLevels, onNodeClick,
146
152
  ]);
147
153
  return (_jsx("svg", { ref: svgRef, style: {
148
154
  top: 0,
@@ -1 +1 @@
1
- {"version":3,"file":"TreemapSubscriber.d.ts","sourceRoot":"","sources":["../src/TreemapSubscriber.js"],"names":[],"mappings":"AAyBA,2DA4OC"}
1
+ {"version":3,"file":"TreemapSubscriber.d.ts","sourceRoot":"","sources":["../src/TreemapSubscriber.js"],"names":[],"mappings":"AAyBA,2DAiPC"}
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  /* eslint-disable no-unused-vars */
3
- import React, { useMemo } from 'react';
3
+ import React, { useMemo, useCallback } from 'react';
4
4
  import { TitleInfo, useCoordination, useLoaders, useUrls, useReady, useGridItemSize, useObsFeatureMatrixIndices, useObsSetsData, useSampleEdgesData, useSampleSetsData, } from '@vitessce/vit-s';
5
5
  import { ViewType, COMPONENT_COORDINATION_TYPES, ViewHelpMapping } from '@vitessce/constants-internal';
6
6
  import { treeToSelectedSetMap, treeToSetSizesBySetNames, mergeObsSets } from '@vitessce/sets-utils';
@@ -99,5 +99,8 @@ export function TreemapSubscriber(props) {
99
99
  sampleSetSelection,
100
100
  // TODO: consider filtering-related coordination values
101
101
  ]);
102
- return (_jsx(TitleInfo, { title: `Treemap of ${capitalize(plur(obsType, 2))}`, info: `${commaNumber(obsCount)} ${plur(obsType, obsCount)} from ${commaNumber(sampleCount)} ${plur(sampleType, sampleCount)}`, removeGridComponent: removeGridComponent, urls: urls, theme: theme, isReady: isReady, helpText: helpText, options: (_jsx(TreemapOptions, { obsType: obsType, sampleType: sampleType, obsColorEncoding: obsColorEncoding, setObsColorEncoding: setObsColorEncoding, hierarchyLevels: hierarchyLevels || DEFAULT_HIERARCHY_LEVELS, setHierarchyLevels: setHierarchyLevels })), children: _jsx("div", { ref: containerRef, className: classes.vegaContainer, children: _jsx(Treemap, { obsCounts: obsCounts, sampleCounts: sampleCounts, obsColorEncoding: obsColorEncoding, hierarchyLevels: hierarchyLevels || DEFAULT_HIERARCHY_LEVELS, theme: theme, width: width, height: height, obsType: obsType, sampleType: sampleType, obsSetColor: obsSetColor, sampleSetColor: sampleSetColor, obsSetSelection: obsSetSelection, sampleSetSelection: sampleSetSelection }) }) }));
102
+ const onNodeClick = useCallback((obsSetPath) => {
103
+ setObsSetSelection([obsSetPath]);
104
+ }, [setObsSetSelection]);
105
+ return (_jsx(TitleInfo, { title: `Treemap of ${capitalize(plur(obsType, 2))}`, info: `${commaNumber(obsCount)} ${plur(obsType, obsCount)} from ${commaNumber(sampleCount)} ${plur(sampleType, sampleCount)}`, removeGridComponent: removeGridComponent, urls: urls, theme: theme, isReady: isReady, helpText: helpText, options: (_jsx(TreemapOptions, { obsType: obsType, sampleType: sampleType, obsColorEncoding: obsColorEncoding, setObsColorEncoding: setObsColorEncoding, hierarchyLevels: hierarchyLevels || DEFAULT_HIERARCHY_LEVELS, setHierarchyLevels: setHierarchyLevels })), children: _jsx("div", { ref: containerRef, className: classes.vegaContainer, children: _jsx(Treemap, { obsCounts: obsCounts, sampleCounts: sampleCounts, obsColorEncoding: obsColorEncoding, hierarchyLevels: hierarchyLevels || DEFAULT_HIERARCHY_LEVELS, theme: theme, width: width, height: height, obsType: obsType, sampleType: sampleType, obsSetColor: obsSetColor, sampleSetColor: sampleSetColor, obsSetSelection: obsSetSelection, sampleSetSelection: sampleSetSelection, onNodeClick: onNodeClick }) }) }));
103
106
  }
@@ -1 +1 @@
1
- {"version":3,"file":"VolcanoPlot.d.ts","sourceRoot":"","sources":["../src/VolcanoPlot.js"],"names":[],"mappings":"AAWA,6DA6SC"}
1
+ {"version":3,"file":"VolcanoPlot.d.ts","sourceRoot":"","sources":["../src/VolcanoPlot.js"],"names":[],"mappings":"AAYA,6DA+SC"}
@@ -7,7 +7,8 @@ import { axisBottom, axisLeft } from 'd3-axis';
7
7
  import { extent as d3_extent } from 'd3-array';
8
8
  import { select } from 'd3-selection';
9
9
  import { isEqual } from 'lodash-es';
10
- import { capitalize } from '@vitessce/utils';
10
+ import { capitalize, getDefaultForegroundColor } from '@vitessce/utils';
11
+ import { colorArrayToString } from '@vitessce/sets-utils';
11
12
  import { getColorScale } from './utils.js';
12
13
  export default function VolcanoPlot(props) {
13
14
  const { theme, width, height, obsType, featureType, obsSetsColumnNameMapping, sampleSetsColumnNameMapping, sampleSetSelection, obsSetSelection, obsSetColor, sampleSetColor, data, marginTop = 5, marginRight = 5, marginLeft = 50, marginBottom = 50, onFeatureClick, featurePointSignificanceThreshold, featurePointFoldChangeThreshold, featureLabelSignificanceThreshold, featureLabelFoldChangeThreshold, } = props;
@@ -68,7 +69,7 @@ export default function VolcanoPlot(props) {
68
69
  .call(axisLeft(yScale));
69
70
  // Axis titles
70
71
  const titleG = svg.append('g');
71
- const fgColor = 'black'; // TODO: use theme to determine this
72
+ const fgColor = colorArrayToString(getDefaultForegroundColor(theme));
72
73
  // Y-axis title
73
74
  titleG
74
75
  .append('text')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitessce/statistical-plots",
3
- "version": "3.5.8",
3
+ "version": "3.5.9",
4
4
  "author": "HIDIVE Lab at HMS",
5
5
  "homepage": "http://vitessce.io",
6
6
  "repository": {
@@ -29,12 +29,12 @@
29
29
  "react-aria": "^3.28.0",
30
30
  "internmap": "^2.0.3",
31
31
  "uuid": "^9.0.0",
32
- "@vitessce/constants-internal": "3.5.8",
33
- "@vitessce/sets-utils": "3.5.8",
34
- "@vitessce/utils": "3.5.8",
35
- "@vitessce/vega": "3.5.8",
36
- "@vitessce/vit-s": "3.5.8",
37
- "@vitessce/gl": "3.5.8"
32
+ "@vitessce/constants-internal": "3.5.9",
33
+ "@vitessce/sets-utils": "3.5.9",
34
+ "@vitessce/utils": "3.5.9",
35
+ "@vitessce/vega": "3.5.9",
36
+ "@vitessce/vit-s": "3.5.9",
37
+ "@vitessce/gl": "3.5.9"
38
38
  },
39
39
  "devDependencies": {
40
40
  "react": "^18.0.0",
@@ -140,7 +140,7 @@ export default function FeatureSetEnrichmentBarPlot(props) {
140
140
  x: {
141
141
  field: 'minusLog10p',
142
142
  type: 'quantitative',
143
- title: '- log10 p-value',
143
+ title: '-log10 p-value',
144
144
  },
145
145
  color: {
146
146
  field: 'key',
package/src/Treemap.js CHANGED
@@ -47,6 +47,7 @@ export default function Treemap(props) {
47
47
  marginRight = 5,
48
48
  marginLeft = 80,
49
49
  marginBottom,
50
+ onNodeClick,
50
51
  } = props;
51
52
 
52
53
  const hierarchyData = useMemo(() => {
@@ -154,7 +155,14 @@ export default function Treemap(props) {
154
155
  .attr('fill', d => colorScale(getPathForColoring(d)))
155
156
  .attr('fill-opacity', 0.8)
156
157
  .attr('width', d => d.x1 - d.x0)
157
- .attr('height', d => d.y1 - d.y0);
158
+ .attr('height', d => d.y1 - d.y0)
159
+ .on('click', (e, d) => {
160
+ const obsSetPath = (hierarchyLevels[0] === 'obsSet'
161
+ ? d.parent?.data?.[0]
162
+ : d.data?.[0]
163
+ );
164
+ onNodeClick(obsSetPath);
165
+ });
158
166
 
159
167
  // Append a clipPath to ensure text does not overflow.
160
168
  leaf.append('clipPath')
@@ -184,7 +192,7 @@ export default function Treemap(props) {
184
192
  }, [width, height, marginLeft, marginBottom, theme, marginTop, marginRight,
185
193
  obsType, sampleType, treemapLeaves, sampleSetColor, sampleSetSelection,
186
194
  obsSetSelection, obsSetColor, obsSetColorScale, sampleSetColorScale,
187
- obsColorEncoding, hierarchyLevels,
195
+ obsColorEncoding, hierarchyLevels, onNodeClick,
188
196
  ]);
189
197
 
190
198
  return (
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable no-unused-vars */
2
- import React, { useMemo } from 'react';
2
+ import React, { useMemo, useCallback } from 'react';
3
3
  import {
4
4
  TitleInfo,
5
5
  useCoordination,
@@ -215,6 +215,10 @@ export function TreemapSubscriber(props) {
215
215
  // TODO: consider filtering-related coordination values
216
216
  ]);
217
217
 
218
+ const onNodeClick = useCallback((obsSetPath) => {
219
+ setObsSetSelection([obsSetPath]);
220
+ }, [setObsSetSelection]);
221
+
218
222
  return (
219
223
  <TitleInfo
220
224
  title={`Treemap of ${capitalize(plur(obsType, 2))}`}
@@ -255,6 +259,7 @@ export function TreemapSubscriber(props) {
255
259
  sampleSetColor={sampleSetColor}
256
260
  obsSetSelection={obsSetSelection}
257
261
  sampleSetSelection={sampleSetSelection}
262
+ onNodeClick={onNodeClick}
258
263
  />
259
264
  </div>
260
265
  </TitleInfo>
@@ -6,7 +6,8 @@ import { axisBottom, axisLeft } from 'd3-axis';
6
6
  import { extent as d3_extent } from 'd3-array';
7
7
  import { select } from 'd3-selection';
8
8
  import { isEqual } from 'lodash-es';
9
- import { capitalize } from '@vitessce/utils';
9
+ import { capitalize, getDefaultForegroundColor } from '@vitessce/utils';
10
+ import { colorArrayToString } from '@vitessce/sets-utils';
10
11
  import { getColorScale } from './utils.js';
11
12
 
12
13
  export default function VolcanoPlot(props) {
@@ -37,13 +38,13 @@ export default function VolcanoPlot(props) {
37
38
  const svgRef = useRef();
38
39
 
39
40
  const computedData = useMemo(() => data.map(d => ({
40
- ...d,
41
- df: {
42
- ...d.df,
43
- minusLog10p: d.df.featureSignificance.map(v => -Math.log10(v)),
44
- logFoldChange: d.df.featureFoldChange.map(v => Math.log2(v)),
45
- },
46
- })), [data]);
41
+ ...d,
42
+ df: {
43
+ ...d.df,
44
+ minusLog10p: d.df.featureSignificance.map(v => -Math.log10(v)),
45
+ logFoldChange: d.df.featureFoldChange.map(v => Math.log2(v)),
46
+ },
47
+ })), [data]);
47
48
 
48
49
  const [xExtent, yExtent] = useMemo(() => {
49
50
  if (!computedData) {
@@ -108,7 +109,9 @@ export default function VolcanoPlot(props) {
108
109
 
109
110
  // Axis titles
110
111
  const titleG = svg.append('g');
111
- const fgColor = 'black'; // TODO: use theme to determine this
112
+ const fgColor = colorArrayToString(
113
+ getDefaultForegroundColor(theme),
114
+ );
112
115
 
113
116
  // Y-axis title
114
117
  titleG