Repository: deephaven/deephaven-plugins (language: python)

Issue:
ui.picker Implementation with constant data provided from server
Implement spec defined in #247 for ui.picker with constant data. Specifically, the examples where you are providing a number of constant items, e.g.
```py
from deephaven import ui

def PickerExample():
  # simple picker that takes picker options directly and is controlled
  option, set_option = ui.use_state("Option 2")
  
  picker2 = ui.picker(
      "Option 1",
      "Option 2",
      "Option 3",
      "Option 4",
      selected_key=option,
      on_selection_change=set_option
  )

p = PickerExample()
```


Discussion / hints:
DHC component implemented by: https://github.com/deephaven/web-client-ui/pull/1821

Fix patch:
diff --git a/.gitignore b/.gitignore
index f431fa514..fee0f335b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,6 +17,7 @@ tsconfig.tsbuildinfo
 .stylelintcache
 
 # Ignore the test reports
+coverage/
 junit.xml
 
 # Allow for local overrides of docker-compose.yml. https://docs.docker.com/compose/multiple-compose-files/merge/
diff --git a/docker/data/storage/notebooks/DEMO.md b/docker/data/storage/notebooks/DEMO.md
index 7d7749234..db0c59b92 100644
--- a/docker/data/storage/notebooks/DEMO.md
+++ b/docker/data/storage/notebooks/DEMO.md
@@ -61,6 +61,45 @@ def my_input():
 result = my_input()
 ```
 
+## Picker (string values)
+
+The `ui.picker` component can be used to select from a list of items. Here's a basic example for selecting from a list of string values and displaying the selected key in a text field.
+
+```python
+import deephaven.ui as ui
+from deephaven.ui import use_state
+
+
+@ui.component
+def picker():
+    value, set_value = use_state("")
+
+    # Picker for selecting values
+    pick = ui.picker(
+        "Text 1",
+        "Text 2",
+        "Text 3",
+        label="Text",
+        on_selection_change=set_value,
+        selected_key=value,
+    )
+
+    # Show current selection in a ui.text component
+    text = ui.text("Selection: " + value)
+
+    # Display picker and output in a flex column
+    return ui.flex(
+        pick,
+        text,
+        direction="column",
+        margin=10,
+        gap=10,
+    )
+
+
+p = picker()
+```
+
 ## Using Tables
 
 You can open a Deephaven Table and control it using callbacks, as well. Let\'s create a table with some data, and then create a component that allows us to filter the table, and a button group it or ungroup it.
diff --git a/package-lock.json b/package-lock.json
index 5c1aa7bbc..3ece149cb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -31636,23 +31636,6 @@
       "dev": true,
       "peer": true
     },
-    "node_modules/stylelint/node_modules/brace-expansion": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
-      "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
-      "dev": true,
-      "peer": true,
-      "dependencies": {
-        "balanced-match": "^1.0.0"
-      }
-    },
-    "node_modules/stylelint/node_modules/brace-expansion/node_modules/balanced-match": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
-      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
-      "dev": true,
-      "peer": true
-    },
     "node_modules/stylelint/node_modules/cosmiconfig": {
       "version": "9.0.0",
       "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz",
@@ -31694,43 +31677,19 @@
       }
     },
     "node_modules/stylelint/node_modules/flat-cache": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.0.tgz",
-      "integrity": "sha512-EryKbCE/wxpxKniQlyas6PY1I9vwtF3uCBweX+N8KYTCn3Y12RTGtQAJ/bd5pl7kxUAc8v/R3Ake/N17OZiFqA==",
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+      "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
       "dev": true,
       "peer": true,
       "dependencies": {
         "flatted": "^3.2.9",
-        "keyv": "^4.5.4",
-        "rimraf": "^5.0.5"
+        "keyv": "^4.5.4"
       },
       "engines": {
         "node": ">=16"
       }
     },
-    "node_modules/stylelint/node_modules/glob": {
-      "version": "10.3.10",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
-      "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
-      "dev": true,
-      "peer": true,
-      "dependencies": {
-        "foreground-child": "^3.1.0",
-        "jackspeak": "^2.3.5",
-        "minimatch": "^9.0.1",
-        "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
-        "path-scurry": "^1.10.1"
-      },
-      "bin": {
-        "glob": "dist/esm/bin.mjs"
-      },
-      "engines": {
-        "node": ">=16 || 14 >=14.17"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
     "node_modules/stylelint/node_modules/meow": {
       "version": "13.2.0",
       "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz",
@@ -31744,22 +31703,6 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/stylelint/node_modules/minimatch": {
-      "version": "9.0.3",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
-      "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
-      "dev": true,
-      "peer": true,
-      "dependencies": {
-        "brace-expansion": "^2.0.1"
-      },
-      "engines": {
-        "node": ">=16 || 14 >=14.17"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
     "node_modules/stylelint/node_modules/resolve-from": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
@@ -31770,25 +31713,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/stylelint/node_modules/rimraf": {
-      "version": "5.0.5",
-      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz",
-      "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==",
-      "dev": true,
-      "peer": true,
-      "dependencies": {
-        "glob": "^10.3.7"
-      },
-      "bin": {
-        "rimraf": "dist/esm/bin.mjs"
-      },
-      "engines": {
-        "node": ">=14"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
     "node_modules/stylelint/node_modules/signal-exit": {
       "version": "4.1.0",
       "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
@@ -35403,21 +35327,21 @@
       "version": "0.8.0",
       "license": "Apache-2.0",
       "dependencies": {
-        "@adobe/react-spectrum": "^3.29.0",
-        "@deephaven/chart": "^0.66.1",
-        "@deephaven/components": "^0.66.1",
-        "@deephaven/dashboard": "^0.66.1",
-        "@deephaven/dashboard-core-plugins": "^0.66.1",
-        "@deephaven/icons": "^0.66.0",
-        "@deephaven/iris-grid": "^0.66.1",
-        "@deephaven/jsapi-bootstrap": "^0.66.1",
-        "@deephaven/jsapi-components": "^0.66.1",
-        "@deephaven/jsapi-types": "^0.66.0",
-        "@deephaven/log": "^0.66.0",
-        "@deephaven/plugin": "^0.66.1",
-        "@deephaven/react-hooks": "^0.66.0",
-        "@deephaven/redux": "^0.66.1",
-        "@deephaven/utils": "^0.66.0",
+        "@adobe/react-spectrum": "^3.34.1",
+        "@deephaven/chart": "^0.67.0",
+        "@deephaven/components": "^0.67.0",
+        "@deephaven/dashboard": "^0.67.0",
+        "@deephaven/dashboard-core-plugins": "^0.67.0",
+        "@deephaven/icons": "^0.67.0",
+        "@deephaven/iris-grid": "^0.67.0",
+        "@deephaven/jsapi-bootstrap": "^0.67.0",
+        "@deephaven/jsapi-components": "^0.67.0",
+        "@deephaven/jsapi-types": "^0.67.0",
+        "@deephaven/log": "^0.67.0",
+        "@deephaven/plugin": "^0.67.0",
+        "@deephaven/react-hooks": "^0.67.0",
+        "@deephaven/redux": "^0.67.0",
+        "@deephaven/utils": "^0.67.0",
         "@fortawesome/react-fontawesome": "^0.2.0",
         "@react-types/shared": "^3.22.0",
         "json-rpc-2.0": "^1.6.0",
@@ -35438,23 +35362,23 @@
       }
     },
     "plugins/ui/src/js/node_modules/@deephaven/chart": {
-      "version": "0.66.1",
-      "resolved": "https://registry.npmjs.org/@deephaven/chart/-/chart-0.66.1.tgz",
-      "integrity": "sha512-2svx9FcuDuJiH6Va+82weQUB/n2g9vjeunanNf6hN8Bgyo87NpWFI3ExK15iEaPYE+oO85FUkPOEtefDcqbpuw==",
-      "dependencies": {
-        "@deephaven/components": "^0.66.1",
-        "@deephaven/icons": "^0.66.0",
-        "@deephaven/jsapi-types": "^0.66.0",
-        "@deephaven/jsapi-utils": "^0.66.0",
-        "@deephaven/log": "^0.66.0",
-        "@deephaven/react-hooks": "^0.66.0",
-        "@deephaven/utils": "^0.66.0",
+      "version": "0.67.0",
+      "resolved": "https://registry.npmjs.org/@deephaven/chart/-/chart-0.67.0.tgz",
+      "integrity": "sha512-Jkq9Lh647JWRvUSuqKnyqeMWjsSDDQY+uUpcAkw9F4J2Fl+j80TcG6Pk9xyocEJ1KwvCVojNtZbDlQHHPkVIMQ==",
+      "dependencies": {
+        "@deephaven/components": "^0.67.0",
+        "@deephaven/icons": "^0.67.0",
+        "@deephaven/jsapi-types": "^0.67.0",
+        "@deephaven/jsapi-utils": "^0.67.0",
+        "@deephaven/log": "^0.67.0",
+        "@deephaven/react-hooks": "^0.67.0",
+        "@deephaven/utils": "^0.67.0",
         "deep-equal": "^2.0.5",
         "lodash.debounce": "^4.0.8",
         "lodash.set": "^4.3.2",
         "memoize-one": "^5.1.1",
         "memoizee": "^0.4.15",
-        "plotly.js": "^2.18.2",
+        "plotly.js": "^2.29.1",
         "prop-types": "^15.7.2",
         "react-plotly.js": "^2.6.0"
       },
@@ -35466,18 +35390,19 @@
       }
     },
     "plugins/ui/src/js/node_modules/@deephaven/components": {
-      "version": "0.66.1",
-      "resolved": "https://registry.npmjs.org/@deephaven/components/-/components-0.66.1.tgz",
-      "integrity": "sha512-r2NfEI9LXkK5izL6SOQugmtuDv8Q06jOtBWD/uYcl4aE6moACZU8QoxUwMVQhmA0MMwlzZ6lJ0ZVVmxrz6an9w==",
+      "version": "0.67.0",
+      "resolved": "https://registry.npmjs.org/@deephaven/components/-/components-0.67.0.tgz",
+      "integrity": "sha512-wpyBFL+/ePadhtxFu2+lG55UbWfzcwGyFtarLL7x3aVJdTTcwi5YtgUdYxHgbpmBfhG+mht2aLU59mLh2dhZ0A==",
       "dependencies": {
         "@adobe/react-spectrum": "^3.34.1",
-        "@deephaven/icons": "^0.66.0",
-        "@deephaven/log": "^0.66.0",
-        "@deephaven/react-hooks": "^0.66.0",
-        "@deephaven/utils": "^0.66.0",
+        "@deephaven/icons": "^0.67.0",
+        "@deephaven/log": "^0.67.0",
+        "@deephaven/react-hooks": "^0.67.0",
+        "@deephaven/utils": "^0.67.0",
         "@fortawesome/fontawesome-svg-core": "^6.2.1",
         "@fortawesome/react-fontawesome": "^0.2.0",
         "@react-spectrum/theme-default": "^3.5.1",
+        "@react-types/shared": "^3.22.1",
         "bootstrap": "4.6.2",
         "classnames": "^2.3.1",
         "event-target-shim": "^6.0.2",
@@ -35502,19 +35427,19 @@
       }
     },
     "plugins/ui/src/js/node_modules/@deephaven/console": {
-      "version": "0.66.1",
-      "resolved": "https://registry.npmjs.org/@deephaven/console/-/console-0.66.1.tgz",
-      "integrity": "sha512-Jx7hr9YBv63kNJabxU4HJNEsitNXgQByU3Ilq6Rle5TFg66VdZhO0WJVlnyH+kMgU9UIEeYp0FivzCW3PB77eA==",
-      "dependencies": {
-        "@deephaven/chart": "^0.66.1",
-        "@deephaven/components": "^0.66.1",
-        "@deephaven/icons": "^0.66.0",
-        "@deephaven/jsapi-bootstrap": "^0.66.1",
-        "@deephaven/jsapi-types": "^0.66.0",
-        "@deephaven/log": "^0.66.0",
-        "@deephaven/react-hooks": "^0.66.0",
-        "@deephaven/storage": "^0.66.0",
-        "@deephaven/utils": "^0.66.0",
+      "version": "0.67.0",
+      "resolved": "https://registry.npmjs.org/@deephaven/console/-/console-0.67.0.tgz",
+      "integrity": "sha512-umMLDPdaAyLafXW35fr7kWk/5ZfEd44lyTgu9GDVVththSLyKj3hpIBUWpfIGekwTrb46wfZbVmRbPKEmZNa4w==",
+      "dependencies": {
+        "@deephaven/chart": "^0.67.0",
+        "@deephaven/components": "^0.67.0",
+        "@deephaven/icons": "^0.67.0",
+        "@deephaven/jsapi-bootstrap": "^0.67.0",
+        "@deephaven/jsapi-types": "^0.67.0",
+        "@deephaven/log": "^0.67.0",
+        "@deephaven/react-hooks": "^0.67.0",
+        "@deephaven/storage": "^0.67.0",
+        "@deephaven/utils": "^0.67.0",
         "@fortawesome/react-fontawesome": "^0.2.0",
         "classnames": "^2.3.1",
         "linkifyjs": "^4.1.0",
@@ -35538,16 +35463,16 @@
       }
     },
     "plugins/ui/src/js/node_modules/@deephaven/dashboard": {
-      "version": "0.66.1",
-      "resolved": "https://registry.npmjs.org/@deephaven/dashboard/-/dashboard-0.66.1.tgz",
-      "integrity": "sha512-YuE1d6S6Q1xYPZY2iuKSuoYD5zMrwMNTnuax+KqCsWxmclcNsB1ZDWxLdAntcByJA3G0Dc6ggSrS01QgZhcFuw==",
-      "dependencies": {
-        "@deephaven/components": "^0.66.1",
-        "@deephaven/golden-layout": "^0.66.1",
-        "@deephaven/log": "^0.66.0",
-        "@deephaven/react-hooks": "^0.66.0",
-        "@deephaven/redux": "^0.66.1",
-        "@deephaven/utils": "^0.66.0",
+      "version": "0.67.0",
+      "resolved": "https://registry.npmjs.org/@deephaven/dashboard/-/dashboard-0.67.0.tgz",
+      "integrity": "sha512-Sx16vqAxPKkF1x1xDfgKnntn7JW37mEBiV1D8qUgCHYUOM+uYiHoh1/UIU/cJfGrdx9ln+vUN21bkLDs++e1iQ==",
+      "dependencies": {
+        "@deephaven/components": "^0.67.0",
+        "@deephaven/golden-layout": "^0.67.0",
+        "@deephaven/log": "^0.67.0",
+        "@deephaven/react-hooks": "^0.67.0",
+        "@deephaven/redux": "^0.67.0",
+        "@deephaven/utils": "^0.67.0",
         "deep-equal": "^2.0.5",
         "lodash.ismatch": "^4.1.1",
         "lodash.throttle": "^4.1.1",
@@ -35565,30 +35490,30 @@
       }
     },
     "plugins/ui/src/js/node_modules/@deephaven/dashboard-core-plugins": {
-      "version": "0.66.1",
-      "resolved": "https://registry.npmjs.org/@deephaven/dashboard-core-plugins/-/dashboard-core-plugins-0.66.1.tgz",
-      "integrity": "sha512-tSv8cFIUoo9qObidyqzOawyHxWi9Y8UdTOxG+HduTc4PyIoZR3rOJIrGAL4okb6bCUVGKuVAqyGz9QMuyCwZYA==",
-      "dependencies": {
-        "@deephaven/chart": "^0.66.1",
-        "@deephaven/components": "^0.66.1",
-        "@deephaven/console": "^0.66.1",
-        "@deephaven/dashboard": "^0.66.1",
-        "@deephaven/file-explorer": "^0.66.1",
-        "@deephaven/filters": "^0.66.0",
-        "@deephaven/golden-layout": "^0.66.1",
-        "@deephaven/grid": "^0.66.0",
-        "@deephaven/icons": "^0.66.0",
-        "@deephaven/iris-grid": "^0.66.1",
-        "@deephaven/jsapi-bootstrap": "^0.66.1",
-        "@deephaven/jsapi-components": "^0.66.1",
-        "@deephaven/jsapi-types": "^0.66.0",
-        "@deephaven/jsapi-utils": "^0.66.0",
-        "@deephaven/log": "^0.66.0",
-        "@deephaven/plugin": "^0.66.1",
-        "@deephaven/react-hooks": "^0.66.0",
-        "@deephaven/redux": "^0.66.1",
-        "@deephaven/storage": "^0.66.0",
-        "@deephaven/utils": "^0.66.0",
+      "version": "0.67.0",
+      "resolved": "https://registry.npmjs.org/@deephaven/dashboard-core-plugins/-/dashboard-core-plugins-0.67.0.tgz",
+      "integrity": "sha512-1XrHcvLclyIZLttFO0hgCncK/jEPT02NTObI/7OHLeTKpl6OgHuzmabYFxB0QrPAWqmlPaURtaDCH/kun4t54A==",
+      "dependencies": {
+        "@deephaven/chart": "^0.67.0",
+        "@deephaven/components": "^0.67.0",
+        "@deephaven/console": "^0.67.0",
+        "@deephaven/dashboard": "^0.67.0",
+        "@deephaven/file-explorer": "^0.67.0",
+        "@deephaven/filters": "^0.67.0",
+        "@deephaven/golden-layout": "^0.67.0",
+        "@deephaven/grid": "^0.67.0",
+        "@deephaven/icons": "^0.67.0",
+        "@deephaven/iris-grid": "^0.67.0",
+        "@deephaven/jsapi-bootstrap": "^0.67.0",
+        "@deephaven/jsapi-components": "^0.67.0",
+        "@deephaven/jsapi-types": "^0.67.0",
+        "@deephaven/jsapi-utils": "^0.67.0",
+        "@deephaven/log": "^0.67.0",
+        "@deephaven/plugin": "^0.67.0",
+        "@deephaven/react-hooks": "^0.67.0",
+        "@deephaven/redux": "^0.67.0",
+        "@deephaven/storage": "^0.67.0",
+        "@deephaven/utils": "^0.67.0",
         "@fortawesome/react-fontawesome": "^0.2.0",
         "classnames": "^2.3.1",
         "deep-equal": "^2.0.5",
@@ -35617,15 +35542,15 @@
       }
     },
     "plugins/ui/src/js/node_modules/@deephaven/file-explorer": {
-      "version": "0.66.1",
-      "resolved": "https://registry.npmjs.org/@deephaven/file-explorer/-/file-explorer-0.66.1.tgz",
-      "integrity": "sha512-HS3E6bBx/u6bYMd85pZcjVrH9Z4dBufBBBzbwECczxnJUZ8rg56VyAef30Bz8aO0lfVxf6Im8S5mxWw3J/53CQ==",
-      "dependencies": {
-        "@deephaven/components": "^0.66.1",
-        "@deephaven/icons": "^0.66.0",
-        "@deephaven/log": "^0.66.0",
-        "@deephaven/storage": "^0.66.0",
-        "@deephaven/utils": "^0.66.0",
+      "version": "0.67.0",
+      "resolved": "https://registry.npmjs.org/@deephaven/file-explorer/-/file-explorer-0.67.0.tgz",
+      "integrity": "sha512-Be4E+PK7lgwzGpOY3rnD2PssY/7cVBVcn7RMq9cPnWAcQZpkQg0IGCYpu+ahmRwqv3joeDiHUS7Vr1Fim73j+Q==",
+      "dependencies": {
+        "@deephaven/components": "^0.67.0",
+        "@deephaven/icons": "^0.67.0",
+        "@deephaven/log": "^0.67.0",
+        "@deephaven/storage": "^0.67.0",
+        "@deephaven/utils": "^0.67.0",
         "@fortawesome/fontawesome-svg-core": "^6.2.1",
         "@fortawesome/react-fontawesome": "^0.2.0",
         "classnames": "^2.3.1",
@@ -35640,19 +35565,19 @@
       }
     },
     "plugins/ui/src/js/node_modules/@deephaven/filters": {
-      "version": "0.66.0",
-      "resolved": "https://registry.npmjs.org/@deephaven/filters/-/filters-0.66.0.tgz",
-      "integrity": "sha512-8hMpkwMhdJdFn35zRvE/6S6R+XS9nNuc0SKz4JGGg7bsZNpuOHt8iYRkxWVu88kbgJIOqMBs+y/38TI8sl1jnA==",
+      "version": "0.67.0",
+      "resolved": "https://registry.npmjs.org/@deephaven/filters/-/filters-0.67.0.tgz",
+      "integrity": "sha512-k/qSHXmvAzfDn/EkA+hpsB5nwB/LUYntDH9lHUDix9ygnvalI72I2LgkQ2BBNri3VTKRGLXdummuWrNDVBvZvA==",
       "engines": {
         "node": ">=16"
       }
     },
     "plugins/ui/src/js/node_modules/@deephaven/golden-layout": {
-      "version": "0.66.1",
-      "resolved": "https://registry.npmjs.org/@deephaven/golden-layout/-/golden-layout-0.66.1.tgz",
-      "integrity": "sha512-8qvRLdAFN3cC3Q4zLd0mGV9h4aW0k30KixoPP2zEC45eANzlHFVr407no3w5NB8oeYFJujQ2ql0ZaYPGdtjxmQ==",
+      "version": "0.67.0",
+      "resolved": "https://registry.npmjs.org/@deephaven/golden-layout/-/golden-layout-0.67.0.tgz",
+      "integrity": "sha512-Yt9cVBOdjNLsFJrk0BH7Ld/Y3IzLX9gU8uzxcOZ13JyVKf8Jl9sIvZA/MvMVUbKwqTMeAcmz2AUVuP5RVLP0jw==",
       "dependencies": {
-        "@deephaven/components": "^0.66.1",
+        "@deephaven/components": "^0.67.0",
         "jquery": "^3.6.0"
       },
       "peerDependencies": {
@@ -35661,11 +35586,11 @@
       }
     },
     "plugins/ui/src/js/node_modules/@deephaven/grid": {
-      "version": "0.66.0",
-      "resolved": "https://registry.npmjs.org/@deephaven/grid/-/grid-0.66.0.tgz",
-      "integrity": "sha512-Zv3BhNJp6d2v8NFGv33OAkOJn9PhfnDktrKOR2TPXzoPbShSf0WiChAF1JDUWSdOewX3tNYZEHFJrR+aVup/gw==",
+      "version": "0.67.0",
+      "resolved": "https://registry.npmjs.org/@deephaven/grid/-/grid-0.67.0.tgz",
+      "integrity": "sha512-/UQf3OjQzCqbZ4+hliUzxPs+oCCK1sdiDblhcXfQnBCFVWlnrIXOQGf+5purPAoa5pfocgnXGTsZG3TEIkfyfQ==",
       "dependencies": {
-        "@deephaven/utils": "^0.66.0",
+        "@deephaven/utils": "^0.67.0",
         "classnames": "^2.3.1",
         "color-convert": "^2.0.1",
         "event-target-shim": "^6.0.2",
@@ -35683,9 +35608,9 @@
       }
     },
     "plugins/ui/src/js/node_modules/@deephaven/icons": {
-      "version": "0.66.0",
-      "resolved": "https://registry.npmjs.org/@deephaven/icons/-/icons-0.66.0.tgz",
-      "integrity": "sha512-+8evWDhQzJUpCnashLN3ybac894ikhZRYiBP+bzyVRa9Fr11gAabw9NpVqPepAr102mJXkpR/5pUIajVW0c2VQ==",
+      "version": "0.67.0",
+      "resolved": "https://registry.npmjs.org/@deephaven/icons/-/icons-0.67.0.tgz",
+      "integrity": "sha512-2m4Ufnwcf+ZWnH0JigPpJbxb4QNdu7RKrjaGt0wXEzml5N8arurx1kWMktOhk6Cz25DYJDCe/RMXuHjTqH2vUQ==",
       "dependencies": {
         "@fortawesome/fontawesome-common-types": "^6.1.1"
       },
@@ -35695,22 +35620,22 @@
       }
     },
     "plugins/ui/src/js/node_modules/@deephaven/iris-grid": {
-      "version": "0.66.1",
-      "resolved": "https://registry.npmjs.org/@deephaven/iris-grid/-/iris-grid-0.66.1.tgz",
-      "integrity": "sha512-6h+hXjNMEw9b2tkDhhVEP0aLHMTJmXh30/Vx5b2tRvve2Uv52qZ4hh/V8cb41GznBO4f2W42Y1Pt67Bnjr0HUQ==",
-      "dependencies": {
-        "@deephaven/components": "^0.66.1",
-        "@deephaven/console": "^0.66.1",
-        "@deephaven/filters": "^0.66.0",
-        "@deephaven/grid": "^0.66.0",
-        "@deephaven/icons": "^0.66.0",
-        "@deephaven/jsapi-components": "^0.66.1",
-        "@deephaven/jsapi-types": "^0.66.0",
-        "@deephaven/jsapi-utils": "^0.66.0",
-        "@deephaven/log": "^0.66.0",
-        "@deephaven/react-hooks": "^0.66.0",
-        "@deephaven/storage": "^0.66.0",
-        "@deephaven/utils": "^0.66.0",
+      "version": "0.67.0",
+      "resolved": "https://registry.npmjs.org/@deephaven/iris-grid/-/iris-grid-0.67.0.tgz",
+      "integrity": "sha512-LMttYlLIRD4NXxBOFc/F36zEsv6C0cZWXmFmeYJdFF1w2qugQpbLubxgu5roJt4qP9ZKNm00IhQs6sVfll1zhg==",
+      "dependencies": {
+        "@deephaven/components": "^0.67.0",
+        "@deephaven/console": "^0.67.0",
+        "@deephaven/filters": "^0.67.0",
+        "@deephaven/grid": "^0.67.0",
+        "@deephaven/icons": "^0.67.0",
+        "@deephaven/jsapi-components": "^0.67.0",
+        "@deephaven/jsapi-types": "^0.67.0",
+        "@deephaven/jsapi-utils": "^0.67.0",
+        "@deephaven/log": "^0.67.0",
+        "@deephaven/react-hooks": "^0.67.0",
+        "@deephaven/storage": "^0.67.0",
+        "@deephaven/utils": "^0.67.0",
         "@dnd-kit/core": "^6.1.0",
         "@dnd-kit/sortable": "^7.0.2",
         "@dnd-kit/utilities": "^3.2.2",
@@ -35737,14 +35662,14 @@
       }
     },
     "plugins/ui/src/js/node_modules/@deephaven/jsapi-bootstrap": {
-      "version": "0.66.1",
-      "resolved": "https://registry.npmjs.org/@deephaven/jsapi-bootstrap/-/jsapi-bootstrap-0.66.1.tgz",
-      "integrity": "sha512-B2LdiPjF+KUc2xh5GwilFcYrkh5pzPIJ6xyj3Bs5OZuG5a6+kP989DtRR5wysezqvrK1icTGJJUOr1YsjtOLRQ==",
+      "version": "0.67.0",
+      "resolved": "https://registry.npmjs.org/@deephaven/jsapi-bootstrap/-/jsapi-bootstrap-0.67.0.tgz",
+      "integrity": "sha512-vFEm6YH33TOO3kQBWCxlxJNCZllGBw2RumkhfijS6JhTRPFQMJeb7l3g71W9j128z5eKp0g5c3rnasuQ/R0/fg==",
       "dependencies": {
-        "@deephaven/components": "^0.66.1",
-        "@deephaven/jsapi-types": "^0.66.0",
-        "@deephaven/log": "^0.66.0",
-        "@deephaven/react-hooks": "^0.66.0"
+        "@deephaven/components": "^0.67.0",
+        "@deephaven/jsapi-types": "^0.67.0",
+        "@deephaven/log": "^0.67.0",
+        "@deephaven/react-hooks": "^0.67.0"
       },
       "engines": {
         "node": ">=16"
@@ -35754,17 +35679,17 @@
       }
     },
     "plugins/ui/src/js/node_modules/@deephaven/jsapi-components": {
-      "version": "0.66.1",
-      "resolved": "https://registry.npmjs.org/@deephaven/jsapi-components/-/jsapi-components-0.66.1.tgz",
-      "integrity": "sha512-WL5g/7I5pDeo/e7h9/JMqogNNt/juK086u2N+VBm6HA/pIAeaNw/+GLYg0MQuXzjtpniX5rP8lA7tA1k2IqBbg==",
-      "dependencies": {
-        "@deephaven/components": "^0.66.1",
-        "@deephaven/jsapi-bootstrap": "^0.66.1",
-        "@deephaven/jsapi-types": "^0.66.0",
-        "@deephaven/jsapi-utils": "^0.66.0",
-        "@deephaven/log": "^0.66.0",
-        "@deephaven/react-hooks": "^0.66.0",
-        "@deephaven/utils": "^0.66.0",
+      "version": "0.67.0",
+      "resolved": "https://registry.npmjs.org/@deephaven/jsapi-components/-/jsapi-components-0.67.0.tgz",
+      "integrity": "sha512-TbdVypvpCHd6eIAnDeDnpIbyLEGq3uB8BSZXGEj1QZhgEZCNUihBKU8PUCr2KcA8gYcYROuEXKFHNvi4NVwRpw==",
+      "dependencies": {
+        "@deephaven/components": "^0.67.0",
+        "@deephaven/jsapi-bootstrap": "^0.67.0",
+        "@deephaven/jsapi-types": "^0.67.0",
+        "@deephaven/jsapi-utils": "^0.67.0",
+        "@deephaven/log": "^0.67.0",
+        "@deephaven/react-hooks": "^0.67.0",
+        "@deephaven/utils": "^0.67.0",
         "@types/js-cookie": "^3.0.3",
         "classnames": "^2.3.2",
         "js-cookie": "^3.0.5",
@@ -35779,22 +35704,22 @@
       }
     },
     "plugins/ui/src/js/node_modules/@deephaven/jsapi-types": {
-      "version": "0.66.0",
-      "resolved": "https://registry.npmjs.org/@deephaven/jsapi-types/-/jsapi-types-0.66.0.tgz",
-      "integrity": "sha512-8svhvxFosIZ4mQSbCYJoEoQKzup4QrNoNTZ/EbD5kYr44ZkJifmCFKks98+oSYMaThviafccPZXZnLv6FGtkeA==",
+      "version": "0.67.0",
+      "resolved": "https://registry.npmjs.org/@deephaven/jsapi-types/-/jsapi-types-0.67.0.tgz",
+      "integrity": "sha512-KOI6tI7NeFSDi3RbW2u75qH5qFoH81AJREOsZqVs0nSdoHuPXqvbwy/cpuh2bJdPLFAJ5fgzcuAw4nB8HQ1MkA==",
       "engines": {
         "node": ">=16"
       }
     },
     "plugins/ui/src/js/node_modules/@deephaven/jsapi-utils": {
-      "version": "0.66.0",
-      "resolved": "https://registry.npmjs.org/@deephaven/jsapi-utils/-/jsapi-utils-0.66.0.tgz",
-      "integrity": "sha512-Pma3GTKadk/iFZgvQswxXQ2yailQRd/nn8mct2ylaDHSCLzcO/FbUtCFxqb4ng52Ao41JROYaZZX3e1cvx+4nQ==",
-      "dependencies": {
-        "@deephaven/filters": "^0.66.0",
-        "@deephaven/jsapi-types": "^0.66.0",
-        "@deephaven/log": "^0.66.0",
-        "@deephaven/utils": "^0.66.0",
+      "version": "0.67.0",
+      "resolved": "https://registry.npmjs.org/@deephaven/jsapi-utils/-/jsapi-utils-0.67.0.tgz",
+      "integrity": "sha512-4s5KLSsM8mF+9Uv8f5ftanWU25WLErn5kKCrxZWBGQgJORMPm834KGiHQeD4HMpeZ84s/gMK4rtiWxJ4u9eaHg==",
+      "dependencies": {
+        "@deephaven/filters": "^0.67.0",
+        "@deephaven/jsapi-types": "^0.67.0",
+        "@deephaven/log": "^0.67.0",
+        "@deephaven/utils": "^0.67.0",
         "lodash.clamp": "^4.0.3",
         "shortid": "^2.2.16"
       },
@@ -35803,9 +35728,9 @@
       }
     },
     "plugins/ui/src/js/node_modules/@deephaven/log": {
-      "version": "0.66.0",
-      "resolved": "https://registry.npmjs.org/@deephaven/log/-/log-0.66.0.tgz",
-      "integrity": "sha512-bWf0JMoWYTPl870D2XNrer2jhB8gaD3lLc9Jpq6ybCO03jgMVvg6c9sPsSeCOhfaL6LDyhc2siP5tzh5Mm38bQ==",
+      "version": "0.67.0",
+      "resolved": "https://registry.npmjs.org/@deephaven/log/-/log-0.67.0.tgz",
+      "integrity": "sha512-tfYUThfC3JXTBXB7bVr8IN0go3xbKxlU42XPKtQaShpRxrXgWDmLAuzHtoqCTTlunYxzXa/m8SAIaJ5qGztN6A==",
       "dependencies": {
         "event-target-shim": "^6.0.2"
       },
@@ -35814,17 +35739,17 @@
       }
     },
     "plugins/ui/src/js/node_modules/@deephaven/plugin": {
-      "version": "0.66.1",
-      "resolved": "https://registry.npmjs.org/@deephaven/plugin/-/plugin-0.66.1.tgz",
-      "integrity": "sha512-CvawlZhCj5eX00FD+leoystyp5nw81nBVgY3HH20wMrNGhSd31vbZ1agEfehvd9uq9RNQqQiExMbZ3DFGMjE8A==",
-      "dependencies": {
-        "@deephaven/components": "^0.66.1",
-        "@deephaven/golden-layout": "^0.66.1",
-        "@deephaven/icons": "^0.66.0",
-        "@deephaven/iris-grid": "^0.66.1",
-        "@deephaven/jsapi-types": "^0.66.0",
-        "@deephaven/log": "^0.66.0",
-        "@deephaven/react-hooks": "^0.66.0",
+      "version": "0.67.0",
+      "resolved": "https://registry.npmjs.org/@deephaven/plugin/-/plugin-0.67.0.tgz",
+      "integrity": "sha512-DiNmjqaclXub0vPVxXWGNQpFkTiwO+m9OqUtGTqIRzggZCK3IfqHFcjzUJx3GlCkf/efLIrJMQQVWPONMudRMw==",
+      "dependencies": {
+        "@deephaven/components": "^0.67.0",
+        "@deephaven/golden-layout": "^0.67.0",
+        "@deephaven/icons": "^0.67.0",
+        "@deephaven/iris-grid": "^0.67.0",
+        "@deephaven/jsapi-types": "^0.67.0",
+        "@deephaven/log": "^0.67.0",
+        "@deephaven/react-hooks": "^0.67.0",
         "@fortawesome/fontawesome-common-types": "^6.1.1",
         "@fortawesome/react-fontawesome": "^0.2.0"
       },
@@ -35836,13 +35761,13 @@
       }
     },
     "plugins/ui/src/js/node_modules/@deephaven/react-hooks": {
-      "version": "0.66.0",
-      "resolved": "https://registry.npmjs.org/@deephaven/react-hooks/-/react-hooks-0.66.0.tgz",
-      "integrity": "sha512-4FfvwHmr8rrMbvl2wabRCXwWZnMwvUGbJitnxhS6eEnAOQ3O/hyJCdYYVOphauz7PqyG8d1f7UTUNAIke3EGvw==",
+      "version": "0.67.0",
+      "resolved": "https://registry.npmjs.org/@deephaven/react-hooks/-/react-hooks-0.67.0.tgz",
+      "integrity": "sha512-WbjYbYhBpXvLnq7TA8ISpIQhP4ViMdv98pFiLfKCW8Sld/5DARG8AcIbYpk+Q4QSsklhr8gGQJNWlzSqMSvz2g==",
       "dependencies": {
         "@adobe/react-spectrum": "^3.34.1",
-        "@deephaven/log": "^0.66.0",
-        "@deephaven/utils": "^0.66.0",
+        "@deephaven/log": "^0.67.0",
+        "@deephaven/utils": "^0.67.0",
         "lodash.debounce": "^4.0.8",
         "shortid": "^2.2.16"
       },
@@ -35854,14 +35779,14 @@
       }
     },
     "plugins/ui/src/js/node_modules/@deephaven/redux": {
-      "version": "0.66.1",
-      "resolved": "https://registry.npmjs.org/@deephaven/redux/-/redux-0.66.1.tgz",
-      "integrity": "sha512-aFLgytw7dGxfhWbMcvm5hVDGaPDPX/c2v2X+o58HtjeakaoxgJyljvp20HeZizvsVghZTDajYn4hDwUDRSb7jg==",
-      "dependencies": {
-        "@deephaven/jsapi-types": "^0.66.0",
-        "@deephaven/jsapi-utils": "^0.66.0",
-        "@deephaven/log": "^0.66.0",
-        "@deephaven/plugin": "^0.66.1",
+      "version": "0.67.0",
+      "resolved": "https://registry.npmjs.org/@deephaven/redux/-/redux-0.67.0.tgz",
+      "integrity": "sha512-wV/2/q4sM/DktKmOGZoyo3c/cIwuGBvIOI3TG7JYpKXUfRyWf9gVxnBwp9wgu3yO3eNYAcq9xW74qYHGtYyqow==",
+      "dependencies": {
+        "@deephaven/jsapi-types": "^0.67.0",
+        "@deephaven/jsapi-utils": "^0.67.0",
+        "@deephaven/log": "^0.67.0",
+        "@deephaven/plugin": "^0.67.0",
         "deep-equal": "^2.0.5",
         "redux-thunk": "2.4.1"
       },
@@ -35873,12 +35798,12 @@
       }
     },
     "plugins/ui/src/js/node_modules/@deephaven/storage": {
-      "version": "0.66.0",
-      "resolved": "https://registry.npmjs.org/@deephaven/storage/-/storage-0.66.0.tgz",
-      "integrity": "sha512-My1IYOsmG6rTQviQxkKaxgKThO/wGKZvSLH8pv47kccUFhgAxlw4SWIQnGgSdX1vrOlLBEoKMrx4kBMO8OWGig==",
+      "version": "0.67.0",
+      "resolved": "https://registry.npmjs.org/@deephaven/storage/-/storage-0.67.0.tgz",
+      "integrity": "sha512-KA/NycHgdqsgY/WDs3srw8Bk4ln3/v17ev3Is8TPOoht+lPddzXvN145CcpyunljKTjd24eWoXXZPBVtuyW81A==",
       "dependencies": {
-        "@deephaven/filters": "^0.66.0",
-        "@deephaven/log": "^0.66.0",
+        "@deephaven/filters": "^0.67.0",
+        "@deephaven/log": "^0.67.0",
         "lodash.throttle": "^4.1.1"
       },
       "engines": {
@@ -35889,9 +35814,9 @@
       }
     },
     "plugins/ui/src/js/node_modules/@deephaven/utils": {
-      "version": "0.66.0",
-      "resolved": "https://registry.npmjs.org/@deephaven/utils/-/utils-0.66.0.tgz",
-      "integrity": "sha512-4d8Aj6CHfRqjs36ogPaAydH2Ir3D4nWp7Ae9ka4cC73hUKB0pNdisTmE3jNkzfggFhFGfLvS+zrYFWl6x8TKQg==",
+      "version": "0.67.0",
+      "resolved": "https://registry.npmjs.org/@deephaven/utils/-/utils-0.67.0.tgz",
+      "integrity": "sha512-bA6pU5QOYDWNZV+fSW8LgDNh/mZ0G3M4zpropcHx1w0iRoLL4xC0b6UgAWCieG9ehi76wsjIDGOo9cy3iYP6WQ==",
       "engines": {
         "node": ">=16"
       }
@@ -39725,21 +39650,21 @@
     "@deephaven/js-plugin-ui": {
       "version": "file:plugins/ui/src/js",
       "requires": {
-        "@adobe/react-spectrum": "^3.29.0",
-        "@deephaven/chart": "^0.66.1",
-        "@deephaven/components": "^0.66.1",
-        "@deephaven/dashboard": "^0.66.1",
-        "@deephaven/dashboard-core-plugins": "^0.66.1",
-        "@deephaven/icons": "^0.66.0",
-        "@deephaven/iris-grid": "^0.66.1",
-        "@deephaven/jsapi-bootstrap": "^0.66.1",
-        "@deephaven/jsapi-components": "^0.66.1",
-        "@deephaven/jsapi-types": "^0.66.0",
-        "@deephaven/log": "^0.66.0",
-        "@deephaven/plugin": "^0.66.1",
-        "@deephaven/react-hooks": "^0.66.0",
-        "@deephaven/redux": "^0.66.1",
-        "@deephaven/utils": "^0.66.0",
+        "@adobe/react-spectrum": "^3.34.1",
+        "@deephaven/chart": "^0.67.0",
+        "@deephaven/components": "^0.67.0",
+        "@deephaven/dashboard": "^0.67.0",
+        "@deephaven/dashboard-core-plugins": "^0.67.0",
+        "@deephaven/icons": "^0.67.0",
+        "@deephaven/iris-grid": "^0.67.0",
+        "@deephaven/jsapi-bootstrap": "^0.67.0",
+        "@deephaven/jsapi-components": "^0.67.0",
+        "@deephaven/jsapi-types": "^0.67.0",
+        "@deephaven/log": "^0.67.0",
+        "@deephaven/plugin": "^0.67.0",
+        "@deephaven/react-hooks": "^0.67.0",
+        "@deephaven/redux": "^0.67.0",
+        "@deephaven/utils": "^0.67.0",
         "@fortawesome/react-fontawesome": "^0.2.0",
         "@react-types/shared": "^3.22.0",
         "@types/react": "^17.0.2",
@@ -39754,40 +39679,41 @@
       },
       "dependencies": {
         "@deephaven/chart": {
-          "version": "0.66.1",
-          "resolved": "https://registry.npmjs.org/@deephaven/chart/-/chart-0.66.1.tgz",
-          "integrity": "sha512-2svx9FcuDuJiH6Va+82weQUB/n2g9vjeunanNf6hN8Bgyo87NpWFI3ExK15iEaPYE+oO85FUkPOEtefDcqbpuw==",
-          "requires": {
-            "@deephaven/components": "^0.66.1",
-            "@deephaven/icons": "^0.66.0",
-            "@deephaven/jsapi-types": "^0.66.0",
-            "@deephaven/jsapi-utils": "^0.66.0",
-            "@deephaven/log": "^0.66.0",
-            "@deephaven/react-hooks": "^0.66.0",
-            "@deephaven/utils": "^0.66.0",
+          "version": "0.67.0",
+          "resolved": "https://registry.npmjs.org/@deephaven/chart/-/chart-0.67.0.tgz",
+          "integrity": "sha512-Jkq9Lh647JWRvUSuqKnyqeMWjsSDDQY+uUpcAkw9F4J2Fl+j80TcG6Pk9xyocEJ1KwvCVojNtZbDlQHHPkVIMQ==",
+          "requires": {
+            "@deephaven/components": "^0.67.0",
+            "@deephaven/icons": "^0.67.0",
+            "@deephaven/jsapi-types": "^0.67.0",
+            "@deephaven/jsapi-utils": "^0.67.0",
+            "@deephaven/log": "^0.67.0",
+            "@deephaven/react-hooks": "^0.67.0",
+            "@deephaven/utils": "^0.67.0",
             "deep-equal": "^2.0.5",
             "lodash.debounce": "^4.0.8",
             "lodash.set": "^4.3.2",
             "memoize-one": "^5.1.1",
             "memoizee": "^0.4.15",
-            "plotly.js": "^2.18.2",
+            "plotly.js": "^2.29.1",
             "prop-types": "^15.7.2",
             "react-plotly.js": "^2.6.0"
           }
         },
         "@deephaven/components": {
-          "version": "0.66.1",
-          "resolved": "https://registry.npmjs.org/@deephaven/components/-/components-0.66.1.tgz",
-          "integrity": "sha512-r2NfEI9LXkK5izL6SOQugmtuDv8Q06jOtBWD/uYcl4aE6moACZU8QoxUwMVQhmA0MMwlzZ6lJ0ZVVmxrz6an9w==",
+          "version": "0.67.0",
+          "resolved": "https://registry.npmjs.org/@deephaven/components/-/components-0.67.0.tgz",
+          "integrity": "sha512-wpyBFL+/ePadhtxFu2+lG55UbWfzcwGyFtarLL7x3aVJdTTcwi5YtgUdYxHgbpmBfhG+mht2aLU59mLh2dhZ0A==",
           "requires": {
             "@adobe/react-spectrum": "^3.34.1",
-            "@deephaven/icons": "^0.66.0",
-            "@deephaven/log": "^0.66.0",
-            "@deephaven/react-hooks": "^0.66.0",
-            "@deephaven/utils": "^0.66.0",
+            "@deephaven/icons": "^0.67.0",
+            "@deephaven/log": "^0.67.0",
+            "@deephaven/react-hooks": "^0.67.0",
+            "@deephaven/utils": "^0.67.0",
             "@fortawesome/fontawesome-svg-core": "^6.2.1",
             "@fortawesome/react-fontawesome": "^0.2.0",
             "@react-spectrum/theme-default": "^3.5.1",
+            "@react-types/shared": "^3.22.1",
             "bootstrap": "4.6.2",
             "classnames": "^2.3.1",
             "event-target-shim": "^6.0.2",
@@ -39805,19 +39731,19 @@
           }
         },
         "@deephaven/console": {
-          "version": "0.66.1",
-          "resolved": "https://registry.npmjs.org/@deephaven/console/-/console-0.66.1.tgz",
-          "integrity": "sha512-Jx7hr9YBv63kNJabxU4HJNEsitNXgQByU3Ilq6Rle5TFg66VdZhO0WJVlnyH+kMgU9UIEeYp0FivzCW3PB77eA==",
-          "requires": {
-            "@deephaven/chart": "^0.66.1",
-            "@deephaven/components": "^0.66.1",
-            "@deephaven/icons": "^0.66.0",
-            "@deephaven/jsapi-bootstrap": "^0.66.1",
-            "@deephaven/jsapi-types": "^0.66.0",
-            "@deephaven/log": "^0.66.0",
-            "@deephaven/react-hooks": "^0.66.0",
-            "@deephaven/storage": "^0.66.0",
-            "@deephaven/utils": "^0.66.0",
+          "version": "0.67.0",
+          "resolved": "https://registry.npmjs.org/@deephaven/console/-/console-0.67.0.tgz",
+          "integrity": "sha512-umMLDPdaAyLafXW35fr7kWk/5ZfEd44lyTgu9GDVVththSLyKj3hpIBUWpfIGekwTrb46wfZbVmRbPKEmZNa4w==",
+          "requires": {
+            "@deephaven/chart": "^0.67.0",
+            "@deephaven/components": "^0.67.0",
+            "@deephaven/icons": "^0.67.0",
+            "@deephaven/jsapi-bootstrap": "^0.67.0",
+            "@deephaven/jsapi-types": "^0.67.0",
+            "@deephaven/log": "^0.67.0",
+            "@deephaven/react-hooks": "^0.67.0",
+            "@deephaven/storage": "^0.67.0",
+            "@deephaven/utils": "^0.67.0",
             "@fortawesome/react-fontawesome": "^0.2.0",
             "classnames": "^2.3.1",
             "linkifyjs": "^4.1.0",
@@ -39834,16 +39760,16 @@
           }
         },
         "@deephaven/dashboard": {
-          "version": "0.66.1",
-          "resolved": "https://registry.npmjs.org/@deephaven/dashboard/-/dashboard-0.66.1.tgz",
-          "integrity": "sha512-YuE1d6S6Q1xYPZY2iuKSuoYD5zMrwMNTnuax+KqCsWxmclcNsB1ZDWxLdAntcByJA3G0Dc6ggSrS01QgZhcFuw==",
-          "requires": {
-            "@deephaven/components": "^0.66.1",
-            "@deephaven/golden-layout": "^0.66.1",
-            "@deephaven/log": "^0.66.0",
-            "@deephaven/react-hooks": "^0.66.0",
-            "@deephaven/redux": "^0.66.1",
-            "@deephaven/utils": "^0.66.0",
+          "version": "0.67.0",
+          "resolved": "https://registry.npmjs.org/@deephaven/dashboard/-/dashboard-0.67.0.tgz",
+          "integrity": "sha512-Sx16vqAxPKkF1x1xDfgKnntn7JW37mEBiV1D8qUgCHYUOM+uYiHoh1/UIU/cJfGrdx9ln+vUN21bkLDs++e1iQ==",
+          "requires": {
+            "@deephaven/components": "^0.67.0",
+            "@deephaven/golden-layout": "^0.67.0",
+            "@deephaven/log": "^0.67.0",
+            "@deephaven/react-hooks": "^0.67.0",
+            "@deephaven/redux": "^0.67.0",
+            "@deephaven/utils": "^0.67.0",
             "deep-equal": "^2.0.5",
             "lodash.ismatch": "^4.1.1",
             "lodash.throttle": "^4.1.1",
@@ -39852,30 +39778,30 @@
           }
         },
         "@deephaven/dashboard-core-plugins": {
-          "version": "0.66.1",
-          "resolved": "https://registry.npmjs.org/@deephaven/dashboard-core-plugins/-/dashboard-core-plugins-0.66.1.tgz",
-          "integrity": "sha512-tSv8cFIUoo9qObidyqzOawyHxWi9Y8UdTOxG+HduTc4PyIoZR3rOJIrGAL4okb6bCUVGKuVAqyGz9QMuyCwZYA==",
-          "requires": {
-            "@deephaven/chart": "^0.66.1",
-            "@deephaven/components": "^0.66.1",
-            "@deephaven/console": "^0.66.1",
-            "@deephaven/dashboard": "^0.66.1",
-            "@deephaven/file-explorer": "^0.66.1",
-            "@deephaven/filters": "^0.66.0",
-            "@deephaven/golden-layout": "^0.66.1",
-            "@deephaven/grid": "^0.66.0",
-            "@deephaven/icons": "^0.66.0",
-            "@deephaven/iris-grid": "^0.66.1",
-            "@deephaven/jsapi-bootstrap": "^0.66.1",
-            "@deephaven/jsapi-components": "^0.66.1",
-            "@deephaven/jsapi-types": "^0.66.0",
-            "@deephaven/jsapi-utils": "^0.66.0",
-            "@deephaven/log": "^0.66.0",
-            "@deephaven/plugin": "^0.66.1",
-            "@deephaven/react-hooks": "^0.66.0",
-            "@deephaven/redux": "^0.66.1",
-            "@deephaven/storage": "^0.66.0",
-            "@deephaven/utils": "^0.66.0",
+          "version": "0.67.0",
+          "resolved": "https://registry.npmjs.org/@deephaven/dashboard-core-plugins/-/dashboard-core-plugins-0.67.0.tgz",
+          "integrity": "sha512-1XrHcvLclyIZLttFO0hgCncK/jEPT02NTObI/7OHLeTKpl6OgHuzmabYFxB0QrPAWqmlPaURtaDCH/kun4t54A==",
+          "requires": {
+            "@deephaven/chart": "^0.67.0",
+            "@deephaven/components": "^0.67.0",
+            "@deephaven/console": "^0.67.0",
+            "@deephaven/dashboard": "^0.67.0",
+            "@deephaven/file-explorer": "^0.67.0",
+            "@deephaven/filters": "^0.67.0",
+            "@deephaven/golden-layout": "^0.67.0",
+            "@deephaven/grid": "^0.67.0",
+            "@deephaven/icons": "^0.67.0",
+            "@deephaven/iris-grid": "^0.67.0",
+            "@deephaven/jsapi-bootstrap": "^0.67.0",
+            "@deephaven/jsapi-components": "^0.67.0",
+            "@deephaven/jsapi-types": "^0.67.0",
+            "@deephaven/jsapi-utils": "^0.67.0",
+            "@deephaven/log": "^0.67.0",
+            "@deephaven/plugin": "^0.67.0",
+            "@deephaven/react-hooks": "^0.67.0",
+            "@deephaven/redux": "^0.67.0",
+            "@deephaven/storage": "^0.67.0",
+            "@deephaven/utils": "^0.67.0",
             "@fortawesome/react-fontawesome": "^0.2.0",
             "classnames": "^2.3.1",
             "deep-equal": "^2.0.5",
@@ -39896,15 +39822,15 @@
           }
         },
         "@deephaven/file-explorer": {
-          "version": "0.66.1",
-          "resolved": "https://registry.npmjs.org/@deephaven/file-explorer/-/file-explorer-0.66.1.tgz",
-          "integrity": "sha512-HS3E6bBx/u6bYMd85pZcjVrH9Z4dBufBBBzbwECczxnJUZ8rg56VyAef30Bz8aO0lfVxf6Im8S5mxWw3J/53CQ==",
-          "requires": {
-            "@deephaven/components": "^0.66.1",
-            "@deephaven/icons": "^0.66.0",
-            "@deephaven/log": "^0.66.0",
-            "@deephaven/storage": "^0.66.0",
-            "@deephaven/utils": "^0.66.0",
+          "version": "0.67.0",
+          "resolved": "https://registry.npmjs.org/@deephaven/file-explorer/-/file-explorer-0.67.0.tgz",
+          "integrity": "sha512-Be4E+PK7lgwzGpOY3rnD2PssY/7cVBVcn7RMq9cPnWAcQZpkQg0IGCYpu+ahmRwqv3joeDiHUS7Vr1Fim73j+Q==",
+          "requires": {
+            "@deephaven/components": "^0.67.0",
+            "@deephaven/icons": "^0.67.0",
+            "@deephaven/log": "^0.67.0",
+            "@deephaven/storage": "^0.67.0",
+            "@deephaven/utils": "^0.67.0",
             "@fortawesome/fontawesome-svg-core": "^6.2.1",
             "@fortawesome/react-fontawesome": "^0.2.0",
             "classnames": "^2.3.1",
@@ -39913,25 +39839,25 @@
           }
         },
         "@deephaven/filters": {
-          "version": "0.66.0",
-          "resolved": "https://registry.npmjs.org/@deephaven/filters/-/filters-0.66.0.tgz",
-          "integrity": "sha512-8hMpkwMhdJdFn35zRvE/6S6R+XS9nNuc0SKz4JGGg7bsZNpuOHt8iYRkxWVu88kbgJIOqMBs+y/38TI8sl1jnA=="
+          "version": "0.67.0",
+          "resolved": "https://registry.npmjs.org/@deephaven/filters/-/filters-0.67.0.tgz",
+          "integrity": "sha512-k/qSHXmvAzfDn/EkA+hpsB5nwB/LUYntDH9lHUDix9ygnvalI72I2LgkQ2BBNri3VTKRGLXdummuWrNDVBvZvA=="
         },
         "@deephaven/golden-layout": {
-          "version": "0.66.1",
-          "resolved": "https://registry.npmjs.org/@deephaven/golden-layout/-/golden-layout-0.66.1.tgz",
-          "integrity": "sha512-8qvRLdAFN3cC3Q4zLd0mGV9h4aW0k30KixoPP2zEC45eANzlHFVr407no3w5NB8oeYFJujQ2ql0ZaYPGdtjxmQ==",
+          "version": "0.67.0",
+          "resolved": "https://registry.npmjs.org/@deephaven/golden-layout/-/golden-layout-0.67.0.tgz",
+          "integrity": "sha512-Yt9cVBOdjNLsFJrk0BH7Ld/Y3IzLX9gU8uzxcOZ13JyVKf8Jl9sIvZA/MvMVUbKwqTMeAcmz2AUVuP5RVLP0jw==",
           "requires": {
-            "@deephaven/components": "^0.66.1",
+            "@deephaven/components": "^0.67.0",
             "jquery": "^3.6.0"
           }
         },
         "@deephaven/grid": {
-          "version": "0.66.0",
-          "resolved": "https://registry.npmjs.org/@deephaven/grid/-/grid-0.66.0.tgz",
-          "integrity": "sha512-Zv3BhNJp6d2v8NFGv33OAkOJn9PhfnDktrKOR2TPXzoPbShSf0WiChAF1JDUWSdOewX3tNYZEHFJrR+aVup/gw==",
+          "version": "0.67.0",
+          "resolved": "https://registry.npmjs.org/@deephaven/grid/-/grid-0.67.0.tgz",
+          "integrity": "sha512-/UQf3OjQzCqbZ4+hliUzxPs+oCCK1sdiDblhcXfQnBCFVWlnrIXOQGf+5purPAoa5pfocgnXGTsZG3TEIkfyfQ==",
           "requires": {
-            "@deephaven/utils": "^0.66.0",
+            "@deephaven/utils": "^0.67.0",
             "classnames": "^2.3.1",
             "color-convert": "^2.0.1",
             "event-target-shim": "^6.0.2",
@@ -39943,30 +39869,30 @@
           }
         },
         "@deephaven/icons": {
-          "version": "0.66.0",
-          "resolved": "https://registry.npmjs.org/@deephaven/icons/-/icons-0.66.0.tgz",
-          "integrity": "sha512-+8evWDhQzJUpCnashLN3ybac894ikhZRYiBP+bzyVRa9Fr11gAabw9NpVqPepAr102mJXkpR/5pUIajVW0c2VQ==",
+          "version": "0.67.0",
+          "resolved": "https://registry.npmjs.org/@deephaven/icons/-/icons-0.67.0.tgz",
+          "integrity": "sha512-2m4Ufnwcf+ZWnH0JigPpJbxb4QNdu7RKrjaGt0wXEzml5N8arurx1kWMktOhk6Cz25DYJDCe/RMXuHjTqH2vUQ==",
           "requires": {
             "@fortawesome/fontawesome-common-types": "^6.1.1"
           }
         },
         "@deephaven/iris-grid": {
-          "version": "0.66.1",
-          "resolved": "https://registry.npmjs.org/@deephaven/iris-grid/-/iris-grid-0.66.1.tgz",
-          "integrity": "sha512-6h+hXjNMEw9b2tkDhhVEP0aLHMTJmXh30/Vx5b2tRvve2Uv52qZ4hh/V8cb41GznBO4f2W42Y1Pt67Bnjr0HUQ==",
-          "requires": {
-            "@deephaven/components": "^0.66.1",
-            "@deephaven/console": "^0.66.1",
-            "@deephaven/filters": "^0.66.0",
-            "@deephaven/grid": "^0.66.0",
-            "@deephaven/icons": "^0.66.0",
-            "@deephaven/jsapi-components": "^0.66.1",
-            "@deephaven/jsapi-types": "^0.66.0",
-            "@deephaven/jsapi-utils": "^0.66.0",
-            "@deephaven/log": "^0.66.0",
-            "@deephaven/react-hooks": "^0.66.0",
-            "@deephaven/storage": "^0.66.0",
-            "@deephaven/utils": "^0.66.0",
+          "version": "0.67.0",
+          "resolved": "https://registry.npmjs.org/@deephaven/iris-grid/-/iris-grid-0.67.0.tgz",
+          "integrity": "sha512-LMttYlLIRD4NXxBOFc/F36zEsv6C0cZWXmFmeYJdFF1w2qugQpbLubxgu5roJt4qP9ZKNm00IhQs6sVfll1zhg==",
+          "requires": {
+            "@deephaven/components": "^0.67.0",
+            "@deephaven/console": "^0.67.0",
+            "@deephaven/filters": "^0.67.0",
+            "@deephaven/grid": "^0.67.0",
+            "@deephaven/icons": "^0.67.0",
+            "@deephaven/jsapi-components": "^0.67.0",
+            "@deephaven/jsapi-types": "^0.67.0",
+            "@deephaven/jsapi-utils": "^0.67.0",
+            "@deephaven/log": "^0.67.0",
+            "@deephaven/react-hooks": "^0.67.0",
+            "@deephaven/storage": "^0.67.0",
+            "@deephaven/utils": "^0.67.0",
             "@dnd-kit/core": "^6.1.0",
             "@dnd-kit/sortable": "^7.0.2",
             "@dnd-kit/utilities": "^3.2.2",
@@ -39986,28 +39912,28 @@
           }
         },
         "@deephaven/jsapi-bootstrap": {
-          "version": "0.66.1",
-          "resolved": "https://registry.npmjs.org/@deephaven/jsapi-bootstrap/-/jsapi-bootstrap-0.66.1.tgz",
-          "integrity": "sha512-B2LdiPjF+KUc2xh5GwilFcYrkh5pzPIJ6xyj3Bs5OZuG5a6+kP989DtRR5wysezqvrK1icTGJJUOr1YsjtOLRQ==",
+          "version": "0.67.0",
+          "resolved": "https://registry.npmjs.org/@deephaven/jsapi-bootstrap/-/jsapi-bootstrap-0.67.0.tgz",
+          "integrity": "sha512-vFEm6YH33TOO3kQBWCxlxJNCZllGBw2RumkhfijS6JhTRPFQMJeb7l3g71W9j128z5eKp0g5c3rnasuQ/R0/fg==",
           "requires": {
-            "@deephaven/components": "^0.66.1",
-            "@deephaven/jsapi-types": "^0.66.0",
-            "@deephaven/log": "^0.66.0",
-            "@deephaven/react-hooks": "^0.66.0"
+            "@deephaven/components": "^0.67.0",
+            "@deephaven/jsapi-types": "^0.67.0",
+            "@deephaven/log": "^0.67.0",
+            "@deephaven/react-hooks": "^0.67.0"
           }
         },
         "@deephaven/jsapi-components": {
-          "version": "0.66.1",
-          "resolved": "https://registry.npmjs.org/@deephaven/jsapi-components/-/jsapi-components-0.66.1.tgz",
-          "integrity": "sha512-WL5g/7I5pDeo/e7h9/JMqogNNt/juK086u2N+VBm6HA/pIAeaNw/+GLYg0MQuXzjtpniX5rP8lA7tA1k2IqBbg==",
-          "requires": {
-            "@deephaven/components": "^0.66.1",
-            "@deephaven/jsapi-bootstrap": "^0.66.1",
-            "@deephaven/jsapi-types": "^0.66.0",
-            "@deephaven/jsapi-utils": "^0.66.0",
-            "@deephaven/log": "^0.66.0",
-            "@deephaven/react-hooks": "^0.66.0",
-            "@deephaven/utils": "^0.66.0",
+          "version": "0.67.0",
+          "resolved": "https://registry.npmjs.org/@deephaven/jsapi-components/-/jsapi-components-0.67.0.tgz",
+          "integrity": "sha512-TbdVypvpCHd6eIAnDeDnpIbyLEGq3uB8BSZXGEj1QZhgEZCNUihBKU8PUCr2KcA8gYcYROuEXKFHNvi4NVwRpw==",
+          "requires": {
+            "@deephaven/components": "^0.67.0",
+            "@deephaven/jsapi-bootstrap": "^0.67.0",
+            "@deephaven/jsapi-types": "^0.67.0",
+            "@deephaven/jsapi-utils": "^0.67.0",
+            "@deephaven/log": "^0.67.0",
+            "@deephaven/react-hooks": "^0.67.0",
+            "@deephaven/utils": "^0.67.0",
             "@types/js-cookie": "^3.0.3",
             "classnames": "^2.3.2",
             "js-cookie": "^3.0.5",
@@ -40016,86 +39942,86 @@
           }
         },
         "@deephaven/jsapi-types": {
-          "version": "0.66.0",
-          "resolved": "https://registry.npmjs.org/@deephaven/jsapi-types/-/jsapi-types-0.66.0.tgz",
-          "integrity": "sha512-8svhvxFosIZ4mQSbCYJoEoQKzup4QrNoNTZ/EbD5kYr44ZkJifmCFKks98+oSYMaThviafccPZXZnLv6FGtkeA=="
+          "version": "0.67.0",
+          "resolved": "https://registry.npmjs.org/@deephaven/jsapi-types/-/jsapi-types-0.67.0.tgz",
+          "integrity": "sha512-KOI6tI7NeFSDi3RbW2u75qH5qFoH81AJREOsZqVs0nSdoHuPXqvbwy/cpuh2bJdPLFAJ5fgzcuAw4nB8HQ1MkA=="
         },
         "@deephaven/jsapi-utils": {
-          "version": "0.66.0",
-          "resolved": "https://registry.npmjs.org/@deephaven/jsapi-utils/-/jsapi-utils-0.66.0.tgz",
-          "integrity": "sha512-Pma3GTKadk/iFZgvQswxXQ2yailQRd/nn8mct2ylaDHSCLzcO/FbUtCFxqb4ng52Ao41JROYaZZX3e1cvx+4nQ==",
-          "requires": {
-            "@deephaven/filters": "^0.66.0",
-            "@deephaven/jsapi-types": "^0.66.0",
-            "@deephaven/log": "^0.66.0",
-            "@deephaven/utils": "^0.66.0",
+          "version": "0.67.0",
+          "resolved": "https://registry.npmjs.org/@deephaven/jsapi-utils/-/jsapi-utils-0.67.0.tgz",
+          "integrity": "sha512-4s5KLSsM8mF+9Uv8f5ftanWU25WLErn5kKCrxZWBGQgJORMPm834KGiHQeD4HMpeZ84s/gMK4rtiWxJ4u9eaHg==",
+          "requires": {
+            "@deephaven/filters": "^0.67.0",
+            "@deephaven/jsapi-types": "^0.67.0",
+            "@deephaven/log": "^0.67.0",
+            "@deephaven/utils": "^0.67.0",
             "lodash.clamp": "^4.0.3",
             "shortid": "^2.2.16"
           }
         },
         "@deephaven/log": {
-          "version": "0.66.0",
-          "resolved": "https://registry.npmjs.org/@deephaven/log/-/log-0.66.0.tgz",
-          "integrity": "sha512-bWf0JMoWYTPl870D2XNrer2jhB8gaD3lLc9Jpq6ybCO03jgMVvg6c9sPsSeCOhfaL6LDyhc2siP5tzh5Mm38bQ==",
+          "version": "0.67.0",
+          "resolved": "https://registry.npmjs.org/@deephaven/log/-/log-0.67.0.tgz",
+          "integrity": "sha512-tfYUThfC3JXTBXB7bVr8IN0go3xbKxlU42XPKtQaShpRxrXgWDmLAuzHtoqCTTlunYxzXa/m8SAIaJ5qGztN6A==",
           "requires": {
             "event-target-shim": "^6.0.2"
           }
         },
         "@deephaven/plugin": {
-          "version": "0.66.1",
-          "resolved": "https://registry.npmjs.org/@deephaven/plugin/-/plugin-0.66.1.tgz",
-          "integrity": "sha512-CvawlZhCj5eX00FD+leoystyp5nw81nBVgY3HH20wMrNGhSd31vbZ1agEfehvd9uq9RNQqQiExMbZ3DFGMjE8A==",
-          "requires": {
-            "@deephaven/components": "^0.66.1",
-            "@deephaven/golden-layout": "^0.66.1",
-            "@deephaven/icons": "^0.66.0",
-            "@deephaven/iris-grid": "^0.66.1",
-            "@deephaven/jsapi-types": "^0.66.0",
-            "@deephaven/log": "^0.66.0",
-            "@deephaven/react-hooks": "^0.66.0",
+          "version": "0.67.0",
+          "resolved": "https://registry.npmjs.org/@deephaven/plugin/-/plugin-0.67.0.tgz",
+          "integrity": "sha512-DiNmjqaclXub0vPVxXWGNQpFkTiwO+m9OqUtGTqIRzggZCK3IfqHFcjzUJx3GlCkf/efLIrJMQQVWPONMudRMw==",
+          "requires": {
+            "@deephaven/components": "^0.67.0",
+            "@deephaven/golden-layout": "^0.67.0",
+            "@deephaven/icons": "^0.67.0",
+            "@deephaven/iris-grid": "^0.67.0",
+            "@deephaven/jsapi-types": "^0.67.0",
+            "@deephaven/log": "^0.67.0",
+            "@deephaven/react-hooks": "^0.67.0",
             "@fortawesome/fontawesome-common-types": "^6.1.1",
             "@fortawesome/react-fontawesome": "^0.2.0"
           }
         },
         "@deephaven/react-hooks": {
-          "version": "0.66.0",
-          "resolved": "https://registry.npmjs.org/@deephaven/react-hooks/-/react-hooks-0.66.0.tgz",
-          "integrity": "sha512-4FfvwHmr8rrMbvl2wabRCXwWZnMwvUGbJitnxhS6eEnAOQ3O/hyJCdYYVOphauz7PqyG8d1f7UTUNAIke3EGvw==",
+          "version": "0.67.0",
+          "resolved": "https://registry.npmjs.org/@deephaven/react-hooks/-/react-hooks-0.67.0.tgz",
+          "integrity": "sha512-WbjYbYhBpXvLnq7TA8ISpIQhP4ViMdv98pFiLfKCW8Sld/5DARG8AcIbYpk+Q4QSsklhr8gGQJNWlzSqMSvz2g==",
           "requires": {
             "@adobe/react-spectrum": "^3.34.1",
-            "@deephaven/log": "^0.66.0",
-            "@deephaven/utils": "^0.66.0",
+            "@deephaven/log": "^0.67.0",
+            "@deephaven/utils": "^0.67.0",
             "lodash.debounce": "^4.0.8",
             "shortid": "^2.2.16"
           }
         },
         "@deephaven/redux": {
-          "version": "0.66.1",
-          "resolved": "https://registry.npmjs.org/@deephaven/redux/-/redux-0.66.1.tgz",
-          "integrity": "sha512-aFLgytw7dGxfhWbMcvm5hVDGaPDPX/c2v2X+o58HtjeakaoxgJyljvp20HeZizvsVghZTDajYn4hDwUDRSb7jg==",
-          "requires": {
-            "@deephaven/jsapi-types": "^0.66.0",
-            "@deephaven/jsapi-utils": "^0.66.0",
-            "@deephaven/log": "^0.66.0",
-            "@deephaven/plugin": "^0.66.1",
+          "version": "0.67.0",
+          "resolved": "https://registry.npmjs.org/@deephaven/redux/-/redux-0.67.0.tgz",
+          "integrity": "sha512-wV/2/q4sM/DktKmOGZoyo3c/cIwuGBvIOI3TG7JYpKXUfRyWf9gVxnBwp9wgu3yO3eNYAcq9xW74qYHGtYyqow==",
+          "requires": {
+            "@deephaven/jsapi-types": "^0.67.0",
+            "@deephaven/jsapi-utils": "^0.67.0",
+            "@deephaven/log": "^0.67.0",
+            "@deephaven/plugin": "^0.67.0",
             "deep-equal": "^2.0.5",
             "redux-thunk": "2.4.1"
           }
         },
         "@deephaven/storage": {
-          "version": "0.66.0",
-          "resolved": "https://registry.npmjs.org/@deephaven/storage/-/storage-0.66.0.tgz",
-          "integrity": "sha512-My1IYOsmG6rTQviQxkKaxgKThO/wGKZvSLH8pv47kccUFhgAxlw4SWIQnGgSdX1vrOlLBEoKMrx4kBMO8OWGig==",
+          "version": "0.67.0",
+          "resolved": "https://registry.npmjs.org/@deephaven/storage/-/storage-0.67.0.tgz",
+          "integrity": "sha512-KA/NycHgdqsgY/WDs3srw8Bk4ln3/v17ev3Is8TPOoht+lPddzXvN145CcpyunljKTjd24eWoXXZPBVtuyW81A==",
           "requires": {
-            "@deephaven/filters": "^0.66.0",
-            "@deephaven/log": "^0.66.0",
+            "@deephaven/filters": "^0.67.0",
+            "@deephaven/log": "^0.67.0",
             "lodash.throttle": "^4.1.1"
           }
         },
         "@deephaven/utils": {
-          "version": "0.66.0",
-          "resolved": "https://registry.npmjs.org/@deephaven/utils/-/utils-0.66.0.tgz",
-          "integrity": "sha512-4d8Aj6CHfRqjs36ogPaAydH2Ir3D4nWp7Ae9ka4cC73hUKB0pNdisTmE3jNkzfggFhFGfLvS+zrYFWl6x8TKQg=="
+          "version": "0.67.0",
+          "resolved": "https://registry.npmjs.org/@deephaven/utils/-/utils-0.67.0.tgz",
+          "integrity": "sha512-bA6pU5QOYDWNZV+fSW8LgDNh/mZ0G3M4zpropcHx1w0iRoLL4xC0b6UgAWCieG9ehi76wsjIDGOo9cy3iYP6WQ=="
         },
         "color-convert": {
           "version": "2.0.1",
@@ -60677,25 +60603,6 @@
           "dev": true,
           "peer": true
         },
-        "brace-expansion": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
-          "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
-          "dev": true,
-          "peer": true,
-          "requires": {
-            "balanced-match": "^1.0.0"
-          },
-          "dependencies": {
-            "balanced-match": {
-              "version": "1.0.2",
-              "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
-              "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
-              "dev": true,
-              "peer": true
-            }
-          }
-        },
         "cosmiconfig": {
           "version": "9.0.0",
           "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz",
@@ -60720,29 +60627,14 @@
           }
         },
         "flat-cache": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.0.tgz",
-          "integrity": "sha512-EryKbCE/wxpxKniQlyas6PY1I9vwtF3uCBweX+N8KYTCn3Y12RTGtQAJ/bd5pl7kxUAc8v/R3Ake/N17OZiFqA==",
+          "version": "4.0.1",
+          "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+          "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
           "dev": true,
           "peer": true,
           "requires": {
             "flatted": "^3.2.9",
-            "keyv": "^4.5.4",
-            "rimraf": "^5.0.5"
-          }
-        },
-        "glob": {
-          "version": "10.3.10",
-          "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
-          "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
-          "dev": true,
-          "peer": true,
-          "requires": {
-            "foreground-child": "^3.1.0",
-            "jackspeak": "^2.3.5",
-            "minimatch": "^9.0.1",
-            "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
-            "path-scurry": "^1.10.1"
+            "keyv": "^4.5.4"
           }
         },
         "meow": {
@@ -60752,16 +60644,6 @@
           "dev": true,
           "peer": true
         },
-        "minimatch": {
-          "version": "9.0.3",
-          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
-          "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
-          "dev": true,
-          "peer": true,
-          "requires": {
-            "brace-expansion": "^2.0.1"
-          }
-        },
         "resolve-from": {
           "version": "5.0.0",
           "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
@@ -60769,16 +60651,6 @@
           "dev": true,
           "peer": true
         },
-        "rimraf": {
-          "version": "5.0.5",
-          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz",
-          "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==",
-          "dev": true,
-          "peer": true,
-          "requires": {
-            "glob": "^10.3.7"
-          }
-        },
         "signal-exit": {
           "version": "4.1.0",
           "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
diff --git a/package.json b/package.json
index 4f7c7f88f..45d85b0e7 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,7 @@
     "test:ci": "run-p test:ci:*",
     "test:ci:unit": "jest --config jest.config.unit.cjs --ci --cacheDirectory $PWD/.jest-cache",
     "test:ci:lint": "jest --config jest.config.lint.cjs --ci --cacheDirectory $PWD/.jest-cache",
-    "update-dh-packages": "lerna run update-dh-packages --"
+    "update-dh-packages": "lerna run update-dh-packages"
   },
   "devDependencies": {
     "@deephaven/babel-preset": "^0.40.0",
diff --git a/plugins/ui/examples/README.md b/plugins/ui/examples/README.md
index 0ef3bd55b..b42560b83 100644
--- a/plugins/ui/examples/README.md
+++ b/plugins/ui/examples/README.md
@@ -92,6 +92,45 @@ ce = checkbox_example()
 
 ![Checkbox](assets/checkbox.png)
 
+## Picker (string values)
+
+The `ui.picker` component can be used to select from a list of items. Here's a basic example for selecting from a list of string values and displaying the selected key in a text field.
+
+```python
+import deephaven.ui as ui
+from deephaven.ui import use_state
+
+
+@ui.component
+def picker():
+    value, set_value = use_state("")
+
+    # Picker for selecting values
+    pick = ui.picker(
+        "Text 1",
+        "Text 2",
+        "Text 3",
+        label="Text",
+        on_selection_change=set_value,
+        selected_key=value,
+    )
+
+    # Show current selection in a ui.text component
+    text = ui.text("Selection: " + value)
+
+    # Display picker and output in a flex column
+    return ui.flex(
+        pick,
+        text,
+        direction="column",
+        margin=10,
+        gap=10,
+    )
+
+
+p = picker()
+```
+
 ## Form (two variables)
 
 You can have state with multiple different variables in one component. In this example, we have a [text field](https://react-spectrum.adobe.com/react-spectrum/TextField.html) and a [slider](https://react-spectrum.adobe.com/react-spectrum/Slider.html), and we display the values of both of them.
diff --git a/plugins/ui/src/js/package.json b/plugins/ui/src/js/package.json
index 7b43d7444..5b5631edd 100644
--- a/plugins/ui/src/js/package.json
+++ b/plugins/ui/src/js/package.json
@@ -41,21 +41,21 @@
     "react-dom": "^17.0.2"
   },
   "dependencies": {
-    "@adobe/react-spectrum": "^3.29.0",
-    "@deephaven/chart": "^0.66.1",
-    "@deephaven/components": "^0.66.1",
-    "@deephaven/dashboard": "^0.66.1",
-    "@deephaven/dashboard-core-plugins": "^0.66.1",
-    "@deephaven/icons": "^0.66.0",
-    "@deephaven/iris-grid": "^0.66.1",
-    "@deephaven/jsapi-bootstrap": "^0.66.1",
-    "@deephaven/jsapi-components": "^0.66.1",
-    "@deephaven/jsapi-types": "^0.66.0",
-    "@deephaven/log": "^0.66.0",
-    "@deephaven/plugin": "^0.66.1",
-    "@deephaven/react-hooks": "^0.66.0",
-    "@deephaven/redux": "^0.66.1",
-    "@deephaven/utils": "^0.66.0",
+    "@adobe/react-spectrum": "^3.34.1",
+    "@deephaven/chart": "^0.67.0",
+    "@deephaven/components": "^0.67.0",
+    "@deephaven/dashboard": "^0.67.0",
+    "@deephaven/dashboard-core-plugins": "^0.67.0",
+    "@deephaven/icons": "^0.67.0",
+    "@deephaven/iris-grid": "^0.67.0",
+    "@deephaven/jsapi-bootstrap": "^0.67.0",
+    "@deephaven/jsapi-components": "^0.67.0",
+    "@deephaven/jsapi-types": "^0.67.0",
+    "@deephaven/log": "^0.67.0",
+    "@deephaven/plugin": "^0.67.0",
+    "@deephaven/react-hooks": "^0.67.0",
+    "@deephaven/redux": "^0.67.0",
+    "@deephaven/utils": "^0.67.0",
     "@fortawesome/react-fontawesome": "^0.2.0",
     "@react-types/shared": "^3.22.0",
     "json-rpc-2.0": "^1.6.0",
diff --git a/plugins/ui/src/js/src/elements/ElementConstants.ts b/plugins/ui/src/js/src/elements/ElementConstants.ts
new file mode 100644
index 000000000..0ff501ac3
--- /dev/null
+++ b/plugins/ui/src/js/src/elements/ElementConstants.ts
@@ -0,0 +1,42 @@
+import { ReactHTML } from 'react';
+import type * as icons from '@deephaven/icons';
+
+/** Namespaces */
+export const UI_COMPONENTS_NAMESPACE = 'deephaven.ui.components';
+export const UI_ELEMENTS_NAMESPACE = 'deephaven.ui.elements';
+
+/** Table */
+export const UITABLE_ELEMENT_TYPE = `${UI_ELEMENTS_NAMESPACE}.UITable` as const;
+export type UITableElementName = typeof UITABLE_ELEMENT_TYPE;
+
+/** Layout */
+export const PANEL_ELEMENT_NAME = `${UI_COMPONENTS_NAMESPACE}.Panel` as const;
+export const ROW_ELEMENT_NAME = `${UI_COMPONENTS_NAMESPACE}.Row` as const;
+export const COLUMN_ELEMENT_NAME = `${UI_COMPONENTS_NAMESPACE}.Column` as const;
+export const STACK_ELEMENT_NAME = `${UI_COMPONENTS_NAMESPACE}.Stack` as const;
+export const DASHBOARD_ELEMENT_NAME =
+  `${UI_COMPONENTS_NAMESPACE}.Dashboard` as const;
+
+export type PanelElementType = typeof PANEL_ELEMENT_NAME;
+export type RowElementType = typeof ROW_ELEMENT_NAME;
+export type ColumnElementType = typeof COLUMN_ELEMENT_NAME;
+export type StackElementType = typeof STACK_ELEMENT_NAME;
+export type DashboardElementType = typeof DASHBOARD_ELEMENT_NAME;
+
+/** Icons */
+export const ICON_ELEMENT_TYPE_PREFIX = 'deephaven.ui.icons.';
+export type IconElementName =
+  `${typeof ICON_ELEMENT_TYPE_PREFIX}${keyof typeof icons}`;
+
+/** HTML */
+export const HTML_ELEMENT_NAME_PREFIX = 'deephaven.ui.html.';
+export type HTMLElementType =
+  `${typeof HTML_ELEMENT_NAME_PREFIX}${keyof ReactHTML}`;
+
+/** Specific Components */
+export const FRAGMENT_ELEMENT_NAME =
+  `${UI_COMPONENTS_NAMESPACE}.Fragment` as const;
+export const ITEM_ELEMENT_NAME = `${UI_COMPONENTS_NAMESPACE}.Item` as const;
+export const PICKER_ELEMENT_NAME = `${UI_COMPONENTS_NAMESPACE}.Picker` as const;
+export const SECTION_ELEMENT_NAME =
+  `${UI_COMPONENTS_NAMESPACE}.Section` as const;
diff --git a/plugins/ui/src/js/src/elements/ElementUtils.ts b/plugins/ui/src/js/src/elements/ElementUtils.ts
index 9fd1357a6..218e6e4c0 100644
--- a/plugins/ui/src/js/src/elements/ElementUtils.ts
+++ b/plugins/ui/src/js/src/elements/ElementUtils.ts
@@ -74,27 +74,3 @@ export function getElementKey(node: unknown, defaultKey: string): string {
   }
   return `${node.props?.key}`;
 }
-
-export const FRAGMENT_ELEMENT_NAME = 'deephaven.ui.components.Fragment';
-
-export type FragmentElementType = typeof FRAGMENT_ELEMENT_NAME;
-
-/**
- * Describes a fragment element that can be rendered in the UI.
- * Will be placed in the current dashboard, or within a user created dashboard if specified.
- */
-export type FragmentElementNode = ElementNode<FragmentElementType>;
-
-/**
- * Check if an object is a FragmentElementNode
- * @param obj Object to check
- * @returns True if the object is a FragmentElementNode
- */
-export function isFragmentElementNode(
-  obj: unknown
-): obj is FragmentElementNode {
-  return (
-    isElementNode(obj) &&
-    (obj as ElementNode)[ELEMENT_KEY] === FRAGMENT_ELEMENT_NAME
-  );
-}
diff --git a/plugins/ui/src/js/src/elements/HTMLElementUtils.ts b/plugins/ui/src/js/src/elements/HTMLElementUtils.ts
index e01124feb..d7926c48c 100644
--- a/plugins/ui/src/js/src/elements/HTMLElementUtils.ts
+++ b/plugins/ui/src/js/src/elements/HTMLElementUtils.ts
@@ -1,11 +1,7 @@
 import { ReactHTML } from 'react';
+import { HTMLElementType, HTML_ELEMENT_NAME_PREFIX } from './ElementConstants';
 import { ELEMENT_KEY, ElementNode, isElementNode } from './ElementUtils';
 
-export const HTML_ELEMENT_NAME_PREFIX = 'deephaven.ui.html.';
-
-export type HTMLElementType =
-  `${typeof HTML_ELEMENT_NAME_PREFIX}${keyof ReactHTML}`;
-
 /**
  * Describes an HTML element that can be rendered in the UI.
  * The tag used for the HTML element is the name of the element without the prefix.
diff --git a/plugins/ui/src/js/src/elements/IconElementUtils.ts b/plugins/ui/src/js/src/elements/IconElementUtils.ts
index 9a6fc2980..871d23a25 100644
--- a/plugins/ui/src/js/src/elements/IconElementUtils.ts
+++ b/plugins/ui/src/js/src/elements/IconElementUtils.ts
@@ -1,11 +1,7 @@
 import * as icons from '@deephaven/icons';
+import { IconElementName, ICON_ELEMENT_TYPE_PREFIX } from './ElementConstants';
 import { ELEMENT_KEY, ElementNode, isElementNode } from './ElementUtils';
 
-export const ICON_ELEMENT_TYPE_PREFIX = 'deephaven.ui.icons.';
-
-export type IconElementName =
-  `${typeof ICON_ELEMENT_TYPE_PREFIX}${keyof typeof icons}`;
-
 /**
  * Describes an icon element that can be rendered in the UI.
  * The name of the icon is the name of the element without the prefix.
diff --git a/plugins/ui/src/js/src/elements/Picker.tsx b/plugins/ui/src/js/src/elements/Picker.tsx
new file mode 100644
index 000000000..d19e34699
--- /dev/null
+++ b/plugins/ui/src/js/src/elements/Picker.tsx
@@ -0,0 +1,15 @@
+import React from 'react';
+import {
+  Picker as DHPicker,
+  PickerProps as DHPickerProps,
+} from '@deephaven/components';
+import { SerializedPickerEventProps, usePickerProps } from './usePickerProps';
+
+function Picker(props: DHPickerProps & SerializedPickerEventProps) {
+  const pickerProps = usePickerProps(props);
+
+  // eslint-disable-next-line react/jsx-props-no-spreading
+  return <DHPicker {...pickerProps} />;
+}
+
+export default Picker;
diff --git a/plugins/ui/src/js/src/elements/UITableUtils.tsx b/plugins/ui/src/js/src/elements/UITableUtils.tsx
index 4bc6a2c11..1d80866bb 100644
--- a/plugins/ui/src/js/src/elements/UITableUtils.tsx
+++ b/plugins/ui/src/js/src/elements/UITableUtils.tsx
@@ -1,10 +1,7 @@
 import type { WidgetExportedObject } from '@deephaven/jsapi-types';
 import { DehydratedSort } from '@deephaven/iris-grid';
 import { ELEMENT_KEY, ElementNode, isElementNode } from './ElementUtils';
-
-export const UITABLE_ELEMENT_TYPE = 'deephaven.ui.elements.UITable';
-
-export type UITableElementName = typeof UITABLE_ELEMENT_TYPE;
+import { UITableElementName, UITABLE_ELEMENT_TYPE } from './ElementConstants';
 
 export interface UITableProps {
   table: WidgetExportedObject;
diff --git a/plugins/ui/src/js/src/elements/usePickerProps.ts b/plugins/ui/src/js/src/elements/usePickerProps.ts
new file mode 100644
index 000000000..5261f3de6
--- /dev/null
+++ b/plugins/ui/src/js/src/elements/usePickerProps.ts
@@ -0,0 +1,48 @@
+import {
+  SerializedFocusEventCallback,
+  useFocusEventCallback,
+} from './spectrum/useFocusEventCallback';
+import {
+  SerializedKeyboardEventCallback,
+  useKeyboardEventCallback,
+} from './spectrum/useKeyboardEventCallback';
+
+export interface SerializedPickerEventProps {
+  /** Handler that is called when the element receives focus. */
+  onFocus?: SerializedFocusEventCallback;
+
+  /** Handler that is called when the element loses focus. */
+  onBlur?: SerializedFocusEventCallback;
+
+  /** Handler that is called when a key is pressed */
+  onKeyDown?: SerializedKeyboardEventCallback;
+
+  /** Handler that is called when a key is released */
+  onKeyUp?: SerializedKeyboardEventCallback;
+}
+
+/**
+ * Wrap Picker props with the appropriate serialized event callbacks.
+ * @param props Props to wrap
+ * @returns Wrapped props
+ */
+export function usePickerProps<T>(props: SerializedPickerEventProps & T) {
+  const { onFocus, onBlur, onKeyDown, onKeyUp, ...otherProps } = props;
+
+  const serializedOnFocus = useFocusEventCallback(onFocus);
+  const serializedOnBlur = useFocusEventCallback(onBlur);
+  const serializedOnKeyDown = useKeyboardEventCallback(onKeyDown);
+  const serializedOnKeyUp = useKeyboardEventCallback(onKeyUp);
+
+  return {
+    onFocus: serializedOnFocus,
+    onBlur: serializedOnBlur,
+    onKeyDown: serializedOnKeyDown,
+    onKeyUp: serializedOnKeyUp,
+    // The @deephaven/components `Picker` has its own normalization logic that
+    // handles primitive children types (string, number, boolean). It also
+    // handles nested children inside of `Item` and `Section` components, so
+    // we are intentionally not wrapping `otherProps` in `mapSpectrumProps`
+    ...otherProps,
+  };
+}
diff --git a/plugins/ui/src/js/src/layout/LayoutUtils.tsx b/plugins/ui/src/js/src/layout/LayoutUtils.tsx
index 230c276d9..459565c05 100644
--- a/plugins/ui/src/js/src/layout/LayoutUtils.tsx
+++ b/plugins/ui/src/js/src/layout/LayoutUtils.tsx
@@ -13,18 +13,18 @@ import Column from './Column';
 import Row from './Row';
 import Stack from './Stack';
 import ReactPanel from './ReactPanel';
-
-export const PANEL_ELEMENT_NAME = 'deephaven.ui.components.Panel';
-export const ROW_ELEMENT_NAME = 'deephaven.ui.components.Row';
-export const COLUMN_ELEMENT_NAME = 'deephaven.ui.components.Column';
-export const STACK_ELEMENT_NAME = 'deephaven.ui.components.Stack';
-export const DASHBOARD_ELEMENT_NAME = 'deephaven.ui.components.Dashboard';
-
-export type PanelElementType = typeof PANEL_ELEMENT_NAME;
-export type RowElementType = typeof ROW_ELEMENT_NAME;
-export type ColumnElementType = typeof COLUMN_ELEMENT_NAME;
-export type StackElementType = typeof STACK_ELEMENT_NAME;
-export type DashboardElementType = typeof DASHBOARD_ELEMENT_NAME;
+import {
+  ColumnElementType,
+  COLUMN_ELEMENT_NAME,
+  DashboardElementType,
+  DASHBOARD_ELEMENT_NAME,
+  PanelElementType,
+  PANEL_ELEMENT_NAME,
+  RowElementType,
+  ROW_ELEMENT_NAME,
+  StackElementType,
+  STACK_ELEMENT_NAME,
+} from '../elements/ElementConstants';
 
 export type GoldenLayoutParent = RowOrColumn | GLStack | Root;
 
diff --git a/plugins/ui/src/js/src/widget/WidgetUtils.tsx b/plugins/ui/src/js/src/widget/WidgetUtils.tsx
index dbe4d5493..cc404b52a 100644
--- a/plugins/ui/src/js/src/widget/WidgetUtils.tsx
+++ b/plugins/ui/src/js/src/widget/WidgetUtils.tsx
@@ -1,10 +1,14 @@
 /* eslint-disable react/jsx-props-no-spreading */
 /* eslint-disable import/prefer-default-export */
-import React from 'react';
+import React, { ComponentType } from 'react';
+// Importing `Item` and `Section` compnents directly since they should not be
+// wrapped due to how Spectrum collection components consume them.
+import { Item, Section } from '@deephaven/components';
 import {
   ElementNode,
+  ELEMENT_KEY,
+  isElementNode,
   isExportedObject,
-  isFragmentElementNode,
 } from '../elements/ElementUtils';
 import HTMLElementView from '../elements/HTMLElementView';
 import { isHTMLElementNode } from '../elements/HTMLElementUtils';
@@ -12,21 +16,50 @@ import { isSpectrumElementNode } from '../elements/SpectrumElementUtils';
 import SpectrumElementView from '../elements/SpectrumElementView';
 import { isIconElementNode } from '../elements/IconElementUtils';
 import IconElementView from '../elements/IconElementView';
-import { isUITable } from '../elements/UITableUtils';
 import UITable from '../elements/UITable';
 import {
-  isColumnElementNode,
-  isDashboardElementNode,
-  isPanelElementNode,
-  isRowElementNode,
-  isStackElementNode,
-} from '../layout/LayoutUtils';
+  COLUMN_ELEMENT_NAME,
+  DASHBOARD_ELEMENT_NAME,
+  FRAGMENT_ELEMENT_NAME,
+  ITEM_ELEMENT_NAME,
+  PANEL_ELEMENT_NAME,
+  PICKER_ELEMENT_NAME,
+  ROW_ELEMENT_NAME,
+  SECTION_ELEMENT_NAME,
+  STACK_ELEMENT_NAME,
+  UITABLE_ELEMENT_TYPE,
+} from '../elements/ElementConstants';
 import ReactPanel from '../layout/ReactPanel';
 import ObjectView from '../elements/ObjectView';
 import Row from '../layout/Row';
 import Stack from '../layout/Stack';
 import Column from '../layout/Column';
 import Dashboard from '../layout/Dashboard';
+import Picker from '../elements/Picker';
+
+/*
+ * Map element node names to their corresponding React components
+ */
+export const elementComponentMap = {
+  [PANEL_ELEMENT_NAME]: ReactPanel,
+  [ROW_ELEMENT_NAME]: Row,
+  [COLUMN_ELEMENT_NAME]: Column,
+  [FRAGMENT_ELEMENT_NAME]: React.Fragment,
+  [STACK_ELEMENT_NAME]: Stack,
+  [DASHBOARD_ELEMENT_NAME]: Dashboard,
+  [ITEM_ELEMENT_NAME]: Item,
+  [PICKER_ELEMENT_NAME]: Picker,
+  [SECTION_ELEMENT_NAME]: Section,
+  [UITABLE_ELEMENT_TYPE]: UITable,
+} as const;
+
+export function getComponentTypeForElement<P extends Record<string, unknown>>(
+  element: ElementNode<string, P>
+): ComponentType<P> | null {
+  return (elementComponentMap[
+    element[ELEMENT_KEY] as keyof typeof elementComponentMap
+  ] ?? null) as ComponentType<P> | null;
+}
 
 export function getComponentForElement(element: ElementNode): React.ReactNode {
   // Need to convert the children of the element if they are exported objects to an ObjectView
@@ -59,27 +92,12 @@ export function getComponentForElement(element: ElementNode): React.ReactNode {
   if (isIconElementNode(newElement)) {
     return IconElementView({ element: newElement });
   }
-  if (isUITable(newElement)) {
-    return <UITable {...newElement.props} />;
-  }
-  if (isPanelElementNode(newElement)) {
-    return <ReactPanel {...newElement.props} />;
-  }
-  if (isFragmentElementNode(newElement)) {
-    // eslint-disable-next-line react/jsx-no-useless-fragment
-    return <>{newElement.props?.children}</>;
-  }
-  if (isRowElementNode(newElement)) {
-    return <Row {...newElement.props} />;
-  }
-  if (isColumnElementNode(newElement)) {
-    return <Column {...newElement.props} />;
-  }
-  if (isStackElementNode(newElement)) {
-    return <Stack {...newElement.props} />;
-  }
-  if (isDashboardElementNode(newElement)) {
-    return <Dashboard {...newElement.props} />;
+  if (isElementNode(newElement)) {
+    const Component = getComponentTypeForElement(newElement);
+
+    if (Component != null) {
+      return <Component {...newElement.props} />;
+    }
   }
 
   return newElement.props?.children;
diff --git a/tools/update-dh-packages.mjs b/tools/update-dh-packages.mjs
index cdabbf7ab..f533e0516 100644
--- a/tools/update-dh-packages.mjs
+++ b/tools/update-dh-packages.mjs
@@ -14,7 +14,7 @@
  * `npm run update-dh-packages`.
  *
  * Or for a specific plugin via:
- * `npm run update-dh-packages --scope=@deephaven/js-plugin-ui`
+ * `npm run update-dh-packages -- --scope=@deephaven/js-plugin-ui`
  */
 
 /* eslint-disable no-console */


Test patch:
diff --git a/plugins/ui/src/js/src/layout/LayoutUtils.test.tsx b/plugins/ui/src/js/src/layout/LayoutUtils.test.tsx
index 14c52e889..da8535034 100644
--- a/plugins/ui/src/js/src/layout/LayoutUtils.test.tsx
+++ b/plugins/ui/src/js/src/layout/LayoutUtils.test.tsx
@@ -3,14 +3,10 @@ import React from 'react';
 import { TestUtils } from '@deephaven/utils';
 import Column from './Column';
 import {
-  PANEL_ELEMENT_NAME,
-  ROW_ELEMENT_NAME,
-  COLUMN_ELEMENT_NAME,
   isPanelElementNode,
   isRowElementNode,
   isColumnElementNode,
   isStackElementNode,
-  STACK_ELEMENT_NAME,
   normalizeDashboardChildren,
   normalizeColumnChildren,
   normalizeRowChildren,
@@ -19,6 +15,12 @@ import {
 import Row from './Row';
 import Stack from './Stack';
 import ReactPanel from './ReactPanel';
+import {
+  PANEL_ELEMENT_NAME,
+  ROW_ELEMENT_NAME,
+  COLUMN_ELEMENT_NAME,
+  STACK_ELEMENT_NAME,
+} from '../elements/ElementConstants';
 
 beforeEach(() => {
   TestUtils.disableConsoleOutput();
diff --git a/plugins/ui/src/js/src/widget/DocumentHandler.test.tsx b/plugins/ui/src/js/src/widget/DocumentHandler.test.tsx
index c00b3286d..5b7023f23 100644
--- a/plugins/ui/src/js/src/widget/DocumentHandler.test.tsx
+++ b/plugins/ui/src/js/src/widget/DocumentHandler.test.tsx
@@ -3,13 +3,13 @@ import { WidgetDescriptor } from '@deephaven/dashboard';
 import { TestUtils } from '@deephaven/utils';
 import { render } from '@testing-library/react';
 import DocumentHandler, { DocumentHandlerProps } from './DocumentHandler';
+import { ReactPanelProps } from '../layout/LayoutUtils';
+import { MixedPanelsError, NoChildrenError } from '../errors';
+import { getComponentForElement } from './WidgetUtils';
 import {
   DASHBOARD_ELEMENT_NAME,
   PANEL_ELEMENT_NAME,
-  ReactPanelProps,
-} from '../layout/LayoutUtils';
-import { MixedPanelsError, NoChildrenError } from '../errors';
-import { getComponentForElement } from './WidgetUtils';
+} from '../elements/ElementConstants';
 
 const mockReactPanel = jest.fn((props: ReactPanelProps) => (
   <div>ReactPanel</div>
diff --git a/plugins/ui/src/js/src/widget/WidgetUtils.test.tsx b/plugins/ui/src/js/src/widget/WidgetUtils.test.tsx
new file mode 100644
index 000000000..4d5ffdbd4
--- /dev/null
+++ b/plugins/ui/src/js/src/widget/WidgetUtils.test.tsx
@@ -0,0 +1,71 @@
+import React from 'react';
+import {
+  FRAGMENT_ELEMENT_NAME,
+  HTML_ELEMENT_NAME_PREFIX,
+  ICON_ELEMENT_TYPE_PREFIX,
+} from '../elements/ElementConstants';
+import { ELEMENT_KEY } from '../elements/ElementUtils';
+import HTMLElementView from '../elements/HTMLElementView';
+import IconElementView from '../elements/IconElementView';
+import { SPECTRUM_ELEMENT_TYPE_PREFIX } from '../elements/SpectrumElementUtils';
+import SpectrumElementView from '../elements/SpectrumElementView';
+import {
+  elementComponentMap,
+  getComponentForElement,
+  getComponentTypeForElement,
+} from './WidgetUtils';
+
+describe('getComponentTypeForElement', () => {
+  it.each(
+    Object.keys(elementComponentMap) as (keyof typeof elementComponentMap)[]
+  )(
+    'should return the correct component type for a given key: %s',
+    elementKey => {
+      const actual = getComponentTypeForElement({ [ELEMENT_KEY]: elementKey });
+      expect(actual).toBe(elementComponentMap[elementKey]);
+    }
+  );
+});
+
+describe('getComponentForElement', () => {
+  it.each([
+    /* eslint-disable react/jsx-key */
+    [`${HTML_ELEMENT_NAME_PREFIX}div`, HTMLElementView],
+    [`${SPECTRUM_ELEMENT_TYPE_PREFIX}ActionButton`, SpectrumElementView],
+    [`${ICON_ELEMENT_TYPE_PREFIX}vsAdd`, IconElementView],
+    /* eslint-enable react/jsx-key */
+  ] as [string, ({ element }: { element: unknown }) => JSX.Element][])(
+    'should use expected element factory function: %s',
+    (elementKey, factory) => {
+      const actual = getComponentForElement({ [ELEMENT_KEY]: elementKey });
+      expect(actual).toEqual(
+        factory({ element: { [ELEMENT_KEY]: elementKey } })
+      );
+    }
+  );
+
+  it.each(
+    Object.keys(elementComponentMap) as (keyof typeof elementComponentMap)[]
+  )('should spread props for element nodes', elementKey => {
+    const element = {
+      [ELEMENT_KEY]: elementKey,
+      props:
+        elementKey === FRAGMENT_ELEMENT_NAME
+          ? { key: 'mock.key', children: ['Some child'] }
+          : {
+              'data-test': 'mock.value',
+              children: ['Some child'],
+            },
+    };
+    const actual = getComponentForElement(element);
+
+    const Expected = elementComponentMap[elementKey] as (
+      props: Record<string, unknown>
+    ) => JSX.Element;
+
+    expect(actual).toEqual(
+      // eslint-disable-next-line react/jsx-props-no-spreading
+      <Expected {...(element.props as Parameters<typeof Expected>[0])} />
+    );
+  });
+});