@xh/hoist 83.1.0 → 84.0.1

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.
Files changed (250) hide show
  1. package/CHANGELOG.md +76 -0
  2. package/admin/tabs/cluster/instances/logs/levels/LogLevelDialogModel.ts +106 -10
  3. package/admin/tabs/cluster/metrics/MetricsModel.ts +3 -3
  4. package/appcontainer/AppContainerModel.ts +1 -1
  5. package/appcontainer/README.md +20 -0
  6. package/assets.d.ts +34 -0
  7. package/build/types/cmp/ag-grid/AgGrid.d.ts +8 -19
  8. package/build/types/cmp/ag-grid/AgGridModel.d.ts +18 -5
  9. package/build/types/cmp/card/Card.d.ts +9 -4
  10. package/build/types/cmp/card/CardModel.d.ts +15 -2
  11. package/build/types/cmp/chart/Chart.d.ts +2 -2
  12. package/build/types/cmp/chart/ChartModel.d.ts +11 -1
  13. package/build/types/cmp/dataview/DataView.d.ts +4 -2
  14. package/build/types/cmp/dataview/DataViewModel.d.ts +16 -4
  15. package/build/types/cmp/filter/FilterChooserModel.d.ts +7 -1
  16. package/build/types/cmp/form/Form.d.ts +2 -1
  17. package/build/types/cmp/form/FormModel.d.ts +12 -0
  18. package/build/types/cmp/form/field/BaseFieldModel.d.ts +7 -0
  19. package/build/types/cmp/form/formfieldset/FormFieldSetModel.d.ts +7 -1
  20. package/build/types/cmp/grid/GridModel.d.ts +16 -1
  21. package/build/types/cmp/grid/GridSorter.d.ts +14 -0
  22. package/build/types/cmp/grid/Types.d.ts +18 -0
  23. package/build/types/cmp/grid/columns/Column.d.ts +40 -2
  24. package/build/types/cmp/grid/columns/ColumnGroup.d.ts +10 -0
  25. package/build/types/cmp/grouping/GroupingChooserModel.d.ts +9 -2
  26. package/build/types/cmp/layout/Box.d.ts +19 -7
  27. package/build/types/cmp/layout/Frame.d.ts +17 -5
  28. package/build/types/cmp/loadingindicator/LoadingIndicator.d.ts +6 -4
  29. package/build/types/cmp/pinpad/PinPadModel.d.ts +6 -1
  30. package/build/types/cmp/spinner/Spinner.d.ts +31 -10
  31. package/build/types/cmp/tab/TabContainerModel.d.ts +11 -0
  32. package/build/types/cmp/tab/TabModel.d.ts +7 -0
  33. package/build/types/cmp/tab/Types.d.ts +4 -0
  34. package/build/types/cmp/treemap/TreeMapModel.d.ts +3 -3
  35. package/build/types/cmp/viewmanager/ViewManagerModel.d.ts +9 -0
  36. package/build/types/cmp/zoneGrid/ZoneGridModel.d.ts +22 -3
  37. package/build/types/cmp/zoneGrid/impl/ZoneMapperModel.d.ts +6 -0
  38. package/build/types/core/HoistComponent.d.ts +29 -8
  39. package/build/types/core/HoistProps.d.ts +9 -3
  40. package/build/types/core/load/LoadSpec.d.ts +1 -1
  41. package/build/types/core/persist/provider/ViewManagerProvider.d.ts +7 -0
  42. package/build/types/data/Store.d.ts +35 -1
  43. package/build/types/data/StoreSelectionModel.d.ts +18 -2
  44. package/build/types/data/cube/Cube.d.ts +26 -6
  45. package/build/types/data/cube/Query.d.ts +10 -0
  46. package/build/types/data/cube/View.d.ts +21 -2
  47. package/build/types/data/cube/aggregate/Aggregator.d.ts +13 -0
  48. package/build/types/data/cube/aggregate/AverageAggregator.d.ts +1 -0
  49. package/build/types/data/cube/aggregate/AverageStrictAggregator.d.ts +1 -0
  50. package/build/types/data/cube/aggregate/ChildCountAggregator.d.ts +1 -0
  51. package/build/types/data/cube/aggregate/LeafCountAggregator.d.ts +1 -0
  52. package/build/types/data/cube/aggregate/MaxAggregator.d.ts +1 -0
  53. package/build/types/data/cube/aggregate/MinAggregator.d.ts +1 -0
  54. package/build/types/data/cube/aggregate/NullAggregator.d.ts +1 -0
  55. package/build/types/data/cube/aggregate/SingleAggregator.d.ts +1 -0
  56. package/build/types/data/cube/aggregate/SumAggregator.d.ts +1 -0
  57. package/build/types/data/cube/aggregate/SumStrictAggregator.d.ts +1 -0
  58. package/build/types/data/cube/aggregate/UniqueAggregator.d.ts +1 -0
  59. package/build/types/data/filter/BaseFilterFieldSpec.d.ts +9 -0
  60. package/build/types/data/filter/Types.d.ts +12 -0
  61. package/build/types/desktop/cmp/button/AppMenuButton.d.ts +5 -0
  62. package/build/types/desktop/cmp/button/Button.d.ts +5 -1
  63. package/build/types/desktop/cmp/dash/canvas/DashCanvasModel.d.ts +12 -3
  64. package/build/types/desktop/cmp/dash/container/DashContainerModel.d.ts +9 -0
  65. package/build/types/desktop/cmp/dock/DockViewModel.d.ts +7 -0
  66. package/build/types/desktop/cmp/filechooser/FileChooserModel.d.ts +8 -0
  67. package/build/types/desktop/cmp/grid/editors/BooleanEditor.d.ts +1 -0
  68. package/build/types/desktop/cmp/grid/editors/DateEditor.d.ts +1 -0
  69. package/build/types/desktop/cmp/grid/editors/NumberEditor.d.ts +1 -0
  70. package/build/types/desktop/cmp/grid/editors/SelectEditor.d.ts +1 -0
  71. package/build/types/desktop/cmp/grid/editors/TextAreaEditor.d.ts +1 -0
  72. package/build/types/desktop/cmp/grid/editors/TextEditor.d.ts +1 -0
  73. package/build/types/desktop/cmp/input/Picker.d.ts +1 -1
  74. package/build/types/desktop/cmp/input/SegmentedControl.d.ts +16 -2
  75. package/build/types/desktop/cmp/leftrightchooser/LeftRightChooserModel.d.ts +7 -0
  76. package/build/types/desktop/cmp/modalsupport/ModalSupportModel.d.ts +28 -2
  77. package/build/types/desktop/cmp/panel/Panel.d.ts +5 -2
  78. package/build/types/desktop/cmp/panel/PanelModel.d.ts +12 -2
  79. package/build/types/desktop/cmp/rest/RestGrid.d.ts +10 -0
  80. package/build/types/desktop/cmp/rest/RestGridModel.d.ts +9 -1
  81. package/build/types/desktop/cmp/toolbar/Toolbar.d.ts +4 -1
  82. package/build/types/format/FormatDate.d.ts +4 -4
  83. package/build/types/icon/Icon.d.ts +3 -0
  84. package/build/types/kit/blueprint/Wrappers.d.ts +12 -1
  85. package/build/types/mobile/cmp/navigator/NavigatorModel.d.ts +8 -0
  86. package/build/types/mobile/cmp/navigator/PageModel.d.ts +7 -0
  87. package/build/types/mobile/cmp/panel/DialogPanel.d.ts +0 -2
  88. package/build/types/security/BaseOAuthClient.d.ts +9 -0
  89. package/build/types/security/authzero/AuthZeroClient.d.ts +6 -0
  90. package/build/types/security/msal/MsalClient.d.ts +6 -0
  91. package/build/types/svc/FetchService.d.ts +10 -7
  92. package/build/types/svc/TraceService.d.ts +17 -2
  93. package/build/types/utils/async/Timer.d.ts +6 -0
  94. package/build/types/utils/js/LangUtils.d.ts +1 -1
  95. package/build/types/utils/js/TestUtils.d.ts +1 -1
  96. package/build/types/utils/react/index.d.ts +0 -1
  97. package/build/types/utils/telemetry/Span.d.ts +12 -2
  98. package/cmp/ag-grid/AgGrid.ts +8 -19
  99. package/cmp/ag-grid/AgGridModel.ts +18 -5
  100. package/cmp/card/Card.ts +9 -4
  101. package/cmp/card/CardModel.ts +15 -2
  102. package/cmp/chart/Chart.ts +2 -2
  103. package/cmp/chart/ChartModel.ts +11 -1
  104. package/cmp/dataview/DataView.ts +4 -2
  105. package/cmp/dataview/DataViewModel.ts +16 -4
  106. package/cmp/filter/FilterChooserModel.ts +7 -1
  107. package/cmp/form/Form.ts +2 -1
  108. package/cmp/form/FormModel.ts +12 -0
  109. package/cmp/form/README.md +13 -0
  110. package/cmp/form/field/BaseFieldModel.ts +7 -0
  111. package/cmp/form/formfieldset/FormFieldSetModel.ts +7 -1
  112. package/cmp/grid/Grid.scss +14 -8
  113. package/cmp/grid/GridModel.ts +16 -1
  114. package/cmp/grid/GridSorter.ts +14 -0
  115. package/cmp/grid/README.md +12 -0
  116. package/cmp/grid/Types.ts +18 -0
  117. package/cmp/grid/columns/Column.ts +40 -2
  118. package/cmp/grid/columns/ColumnGroup.ts +10 -0
  119. package/cmp/grouping/GroupingChooserModel.ts +9 -2
  120. package/cmp/layout/Box.ts +19 -7
  121. package/cmp/layout/Frame.ts +17 -5
  122. package/cmp/layout/README.md +16 -21
  123. package/cmp/loadingindicator/LoadingIndicator.scss +1 -1
  124. package/cmp/loadingindicator/LoadingIndicator.ts +11 -9
  125. package/cmp/pinpad/PinPadModel.ts +6 -1
  126. package/cmp/spinner/Spinner.scss +13 -0
  127. package/cmp/spinner/Spinner.ts +58 -20
  128. package/cmp/tab/TabContainerModel.ts +11 -0
  129. package/cmp/tab/TabModel.ts +7 -0
  130. package/cmp/tab/Types.ts +4 -0
  131. package/cmp/treemap/TreeMapModel.ts +3 -3
  132. package/cmp/viewmanager/ViewManagerModel.ts +9 -0
  133. package/cmp/zoneGrid/ZoneGridModel.ts +22 -3
  134. package/cmp/zoneGrid/impl/ZoneMapperModel.ts +6 -0
  135. package/core/ExceptionHandler.ts +1 -1
  136. package/core/HoistComponent.ts +36 -11
  137. package/core/HoistProps.ts +9 -3
  138. package/core/README.md +68 -6
  139. package/core/impl/InstanceManager.ts +1 -0
  140. package/core/load/LoadSpec.ts +1 -1
  141. package/core/persist/provider/ViewManagerProvider.ts +7 -0
  142. package/data/README.md +48 -124
  143. package/data/Store.ts +35 -1
  144. package/data/StoreSelectionModel.ts +18 -2
  145. package/data/cube/Cube.ts +26 -6
  146. package/data/cube/Query.ts +10 -0
  147. package/data/cube/README.md +236 -0
  148. package/data/cube/View.ts +21 -2
  149. package/data/cube/aggregate/Aggregator.ts +13 -0
  150. package/data/cube/aggregate/AverageAggregator.ts +1 -0
  151. package/data/cube/aggregate/AverageStrictAggregator.ts +1 -0
  152. package/data/cube/aggregate/ChildCountAggregator.ts +1 -0
  153. package/data/cube/aggregate/LeafCountAggregator.ts +1 -0
  154. package/data/cube/aggregate/MaxAggregator.ts +1 -0
  155. package/data/cube/aggregate/MinAggregator.ts +1 -0
  156. package/data/cube/aggregate/NullAggregator.ts +1 -0
  157. package/data/cube/aggregate/SingleAggregator.ts +1 -0
  158. package/data/cube/aggregate/SumAggregator.ts +1 -0
  159. package/data/cube/aggregate/SumStrictAggregator.ts +1 -0
  160. package/data/cube/aggregate/UniqueAggregator.ts +1 -0
  161. package/data/filter/BaseFilterFieldSpec.ts +9 -0
  162. package/data/filter/Types.ts +12 -0
  163. package/desktop/README.md +131 -9
  164. package/desktop/appcontainer/AboutDialog.ts +2 -0
  165. package/desktop/appcontainer/Banner.ts +5 -2
  166. package/desktop/appcontainer/ChangelogDialog.ts +1 -0
  167. package/desktop/appcontainer/ExceptionDialog.ts +4 -0
  168. package/desktop/appcontainer/ExceptionDialogDetails.ts +4 -1
  169. package/desktop/appcontainer/FeedbackDialog.ts +4 -1
  170. package/desktop/appcontainer/ImpersonationBar.ts +4 -0
  171. package/desktop/appcontainer/LockoutPanel.ts +4 -1
  172. package/desktop/appcontainer/LoginPanel.ts +7 -3
  173. package/desktop/appcontainer/Message.ts +9 -3
  174. package/desktop/appcontainer/OptionsDialog.ts +3 -1
  175. package/desktop/appcontainer/VersionBar.ts +1 -0
  176. package/desktop/appcontainer/suspend/IdlePanel.ts +4 -4
  177. package/desktop/appcontainer/suspend/SuspendPanel.ts +3 -0
  178. package/desktop/cmp/button/AppMenuButton.ts +5 -0
  179. package/desktop/cmp/button/Button.ts +14 -4
  180. package/desktop/cmp/dash/README.md +14 -0
  181. package/desktop/cmp/dash/canvas/DashCanvasModel.ts +12 -3
  182. package/desktop/cmp/dash/container/DashContainerModel.ts +9 -0
  183. package/desktop/cmp/dock/DockViewModel.ts +7 -0
  184. package/desktop/cmp/filechooser/FileChooserModel.ts +9 -2
  185. package/desktop/cmp/grid/editors/BooleanEditor.ts +1 -0
  186. package/desktop/cmp/grid/editors/DateEditor.ts +1 -0
  187. package/desktop/cmp/grid/editors/NumberEditor.ts +1 -0
  188. package/desktop/cmp/grid/editors/SelectEditor.ts +1 -0
  189. package/desktop/cmp/grid/editors/TextAreaEditor.ts +1 -0
  190. package/desktop/cmp/grid/editors/TextEditor.ts +1 -0
  191. package/desktop/cmp/input/Picker.ts +2 -2
  192. package/desktop/cmp/input/SegmentedControl.ts +20 -2
  193. package/desktop/cmp/leftrightchooser/LeftRightChooserModel.ts +7 -0
  194. package/desktop/cmp/modalsupport/ModalSupportModel.ts +31 -2
  195. package/desktop/cmp/panel/Panel.ts +29 -21
  196. package/desktop/cmp/panel/PanelModel.ts +12 -2
  197. package/desktop/cmp/panel/README.md +20 -0
  198. package/desktop/cmp/rest/RestGrid.ts +10 -0
  199. package/desktop/cmp/rest/RestGridModel.ts +9 -1
  200. package/desktop/cmp/toolbar/Toolbar.ts +9 -2
  201. package/desktop/cmp/viewmanager/ViewManager.ts +1 -1
  202. package/docs/README.md +9 -4
  203. package/docs/coding-conventions.md +29 -21
  204. package/docs/doc-registry.json +31 -15
  205. package/docs/planning/docs-roadmap-log.md +11 -0
  206. package/docs/planning/docs-roadmap.md +1 -0
  207. package/docs/upgrade-notes/v84-upgrade-notes.md +136 -0
  208. package/docs/version-compatibility.md +2 -0
  209. package/format/FormatDate.ts +4 -4
  210. package/icon/Icon.ts +9 -0
  211. package/icon/README.md +62 -22
  212. package/icon/index.ts +24 -0
  213. package/kit/README.md +8 -2
  214. package/kit/blueprint/Wrappers.ts +12 -1
  215. package/mcp/README.md +47 -26
  216. package/mcp/cli/ts.ts +39 -4
  217. package/mcp/data/ts-registry.ts +57 -17
  218. package/mcp/tools/typescript.ts +32 -4
  219. package/mobile/appcontainer/AboutDialog.ts +3 -0
  220. package/mobile/appcontainer/Banner.ts +2 -0
  221. package/mobile/appcontainer/ExceptionDialog.ts +4 -0
  222. package/mobile/appcontainer/ExceptionDialogDetails.ts +1 -0
  223. package/mobile/appcontainer/FeedbackDialog.ts +4 -1
  224. package/mobile/appcontainer/ImpersonationBar.ts +2 -0
  225. package/mobile/appcontainer/LockoutPanel.ts +2 -0
  226. package/mobile/appcontainer/LoginPanel.ts +7 -3
  227. package/mobile/appcontainer/Message.ts +9 -3
  228. package/mobile/appcontainer/OptionsDialog.ts +5 -1
  229. package/mobile/appcontainer/VersionBar.ts +1 -0
  230. package/mobile/appcontainer/suspend/IdlePanel.ts +5 -6
  231. package/mobile/appcontainer/suspend/SuspendPanel.ts +3 -0
  232. package/mobile/cmp/navigator/NavigatorModel.ts +8 -0
  233. package/mobile/cmp/navigator/PageModel.ts +7 -0
  234. package/mobile/cmp/panel/DialogPanel.ts +0 -2
  235. package/package.json +11 -11
  236. package/security/BaseOAuthClient.ts +9 -0
  237. package/security/authzero/AuthZeroClient.ts +6 -0
  238. package/security/msal/MsalClient.ts +6 -0
  239. package/styles/vars.scss +14 -0
  240. package/svc/FetchService.ts +25 -15
  241. package/svc/README.md +39 -9
  242. package/svc/TraceService.ts +69 -11
  243. package/utils/README.md +0 -1
  244. package/utils/async/Timer.ts +6 -0
  245. package/utils/js/LangUtils.ts +1 -1
  246. package/utils/js/TestUtils.ts +1 -1
  247. package/utils/react/index.ts +0 -1
  248. package/utils/telemetry/Span.ts +21 -4
  249. package/build/types/utils/react/ClassName.d.ts +0 -14
  250. package/utils/react/ClassName.ts +0 -24
package/icon/index.ts CHANGED
@@ -70,6 +70,7 @@ import {
70
70
  faChevronRight as faChevronRightLight,
71
71
  faChevronUp as faChevronUpLight,
72
72
  faCircle as faCircleLight,
73
+ faCircleNotch as faCircleNotchLight,
73
74
  faClipboard as faClipboardLight,
74
75
  faClock as faClockLight,
75
76
  faCloudDownload as faCloudDownloadLight,
@@ -183,6 +184,8 @@ import {
183
184
  faSlidersHSquare as faSlidersHSquareLight,
184
185
  faSparkles as faSparklesLight,
185
186
  faSpinner as faSpinnerLight,
187
+ faSpinnerScale as faSpinnerScaleLight,
188
+ faSpinnerThird as faSpinnerThirdLight,
186
189
  faSquare as faSquareLight,
187
190
  faSquareMinus as faSquareMinusLight,
188
191
  faStar as faStarLight,
@@ -283,6 +286,7 @@ import {
283
286
  faChevronRight,
284
287
  faChevronUp,
285
288
  faCircle,
289
+ faCircleNotch,
286
290
  faClipboard,
287
291
  faClock,
288
292
  faCloudDownload,
@@ -395,6 +399,8 @@ import {
395
399
  faSlidersHSquare,
396
400
  faSparkles,
397
401
  faSpinner,
402
+ faSpinnerScale,
403
+ faSpinnerThird,
398
404
  faSquare,
399
405
  faSquareMinus,
400
406
  faStar,
@@ -496,6 +502,7 @@ import {
496
502
  faChevronRight as faChevronRightSolid,
497
503
  faChevronUp as faChevronUpSolid,
498
504
  faCircle as faCircleSolid,
505
+ faCircleNotch as faCircleNotchSolid,
499
506
  faClipboard as faClipboardSolid,
500
507
  faClock as faClockSolid,
501
508
  faCloudDownload as faCloudDownloadSolid,
@@ -609,6 +616,8 @@ import {
609
616
  faSlidersHSquare as faSlidersHSquareSolid,
610
617
  faSparkles as faSparklesSolid,
611
618
  faSpinner as faSpinnerSolid,
619
+ faSpinnerScale as faSpinnerScaleSolid,
620
+ faSpinnerThird as faSpinnerThirdSolid,
612
621
  faSquare as faSquareSolid,
613
622
  faSquareMinus as faSquareMinusSolid,
614
623
  faStar as faStarSolid,
@@ -710,6 +719,7 @@ import {
710
719
  faChevronRight as faChevronRightThin,
711
720
  faChevronUp as faChevronUpThin,
712
721
  faCircle as faCircleThin,
722
+ faCircleNotch as faCircleNotchThin,
713
723
  faClipboard as faClipboardThin,
714
724
  faClock as faClockThin,
715
725
  faCloudDownload as faCloudDownloadThin,
@@ -823,6 +833,8 @@ import {
823
833
  faSlidersHSquare as faSlidersHSquareThin,
824
834
  faSparkles as faSparklesThin,
825
835
  faSpinner as faSpinnerThin,
836
+ faSpinnerScale as faSpinnerScaleThin,
837
+ faSpinnerThird as faSpinnerThirdThin,
826
838
  faSquare as faSquareThin,
827
839
  faSquareMinus as faSquareMinusThin,
828
840
  faStar as faStarThin,
@@ -1104,6 +1116,10 @@ library.add(
1104
1116
  faCircleLight,
1105
1117
  faCircleSolid,
1106
1118
  faCircleThin,
1119
+ faCircleNotch,
1120
+ faCircleNotchLight,
1121
+ faCircleNotchSolid,
1122
+ faCircleNotchThin,
1107
1123
  faClipboard,
1108
1124
  faClipboardLight,
1109
1125
  faClipboardSolid,
@@ -1556,6 +1572,14 @@ library.add(
1556
1572
  faSpinnerLight,
1557
1573
  faSpinnerSolid,
1558
1574
  faSpinnerThin,
1575
+ faSpinnerScale,
1576
+ faSpinnerScaleLight,
1577
+ faSpinnerScaleSolid,
1578
+ faSpinnerScaleThin,
1579
+ faSpinnerThird,
1580
+ faSpinnerThirdLight,
1581
+ faSpinnerThirdSolid,
1582
+ faSpinnerThirdThin,
1559
1583
  faSquare,
1560
1584
  faSquareLight,
1561
1585
  faSquareSolid,
package/kit/README.md CHANGED
@@ -177,8 +177,14 @@ import {button} from '@xh/hoist/desktop/cmp/button';
177
177
  import {button} from '@xh/hoist/kit/blueprint';
178
178
  ```
179
179
 
180
- The exception is if you need a Blueprint or other library component that Hoist does not wrap (e.g.
181
- `Tree`, `EditableText`).
180
+ The notable exception is **Blueprint's `dialog`** Hoist does not yet provide its own Dialog
181
+ component wrapper ([#861](https://github.com/xh/hoist-react/issues/861)), so applications import
182
+ `dialog` directly from `@xh/hoist/kit/blueprint` for custom modal dialogs. This is the
183
+ established pattern used throughout the framework and applications. See the
184
+ [Dialogs section](../desktop/README.md#dialogs) of the desktop README for usage guidance.
185
+
186
+ Other Blueprint or library components that Hoist does not wrap (e.g. `Tree`, `EditableText`)
187
+ can also be imported from Kit when needed.
182
188
 
183
189
  ## Common Pitfalls
184
190
 
@@ -120,7 +120,6 @@ export const alert = elementFactory(Alert),
120
120
  button = elementFactory(Button),
121
121
  controlGroup = elementFactory(ControlGroup),
122
122
  checkbox = elementFactory(Checkbox),
123
- dialog = elementFactory(Dialog),
124
123
  datePicker = elementFactory(DatePicker),
125
124
  menuDivider = elementFactory(MenuDivider),
126
125
  menuItem = elementFactory(MenuItem),
@@ -161,3 +160,15 @@ export const buttonGroup = elementFactory(ButtonGroup),
161
160
  tab = elementFactory(Tab),
162
161
  tag = elementFactory(Tag),
163
162
  text = elementFactory(Text);
163
+
164
+ /**
165
+ * Blueprint Dialog wrapped with transitions disabled. This is the standard way to create
166
+ * custom modal dialogs in Hoist apps - import from `@xh/hoist/kit/blueprint` rather than
167
+ * from Blueprint directly.
168
+ *
169
+ * Pair with a HoistModel that manages an observable `isOpen` property. The parent component
170
+ * renders the dialog factory alongside its other children - it shows/hides based on `isOpen`.
171
+ *
172
+ * See the desktop package README (`desktop/README.md#dialogs`) for the full usage pattern.
173
+ */
174
+ export const dialog = elementFactory(Dialog);
package/mcp/README.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # MCP Server and CLI Tools
2
2
 
3
+ | Section | Description |
4
+ |---------|-------------|
5
+ | [Overview](#overview) | Purpose, audience, and design rationale |
6
+ | [Architecture](#architecture) | Directory structure, data flow, and design decisions |
7
+ | [CLI Tools](#cli-tools) | `hoist-docs` and `hoist-ts` shell commands |
8
+ | [MCP Server Setup](#mcp-server-setup) | Prerequisites, startup methods, and debug logging |
9
+ | [MCP Tools Reference](#mcp-tools-reference) | Documentation and TypeScript tool APIs |
10
+ | [MCP Resources](#mcp-resources) | Direct URI-based access to documentation files |
11
+ | [Maintaining the Developer Tools](#maintaining-the-developer-tools) | Registry sync, maintenance checklist, and update points |
12
+ | [Extending the Developer Tools](#extending-the-developer-tools) | Adding new tools, resources, and doc registry entries |
13
+ | [Common Pitfalls](#common-pitfalls) | Stdout corruption, path traversal, and naming conventions |
14
+
3
15
  ## Overview
4
16
 
5
17
  The Hoist developer tools give AI coding assistants structured access to hoist-react's documentation
@@ -169,14 +181,15 @@ Run `npx hoist-docs --help` for full usage.
169
181
  ### `hoist-ts` -- TypeScript Symbol Exploration
170
182
 
171
183
  ```bash
172
- # Search for symbols and class members by name
184
+ # Search for symbols by name, keyword, or multi-word query
173
185
  npx hoist-ts search GridModel
174
186
  npx hoist-ts search lastLoadCompleted
175
187
  npx hoist-ts search Store --kind class
188
+ npx hoist-ts search "panel modal" # Matches name + JSDoc content
176
189
 
177
190
  # Get detailed type information for a symbol
178
191
  npx hoist-ts symbol GridModel
179
- npx hoist-ts symbol HoistModel
192
+ npx hoist-ts symbol View --file data/cube/View.ts # Disambiguate by file path
180
193
 
181
194
  # List all members of a class or interface
182
195
  npx hoist-ts members GridModel
@@ -293,13 +306,15 @@ Verify the MCP server is running and responsive. Takes no parameters.
293
306
 
294
307
  #### `hoist-search-symbols`
295
308
 
296
- Search for TypeScript classes, interfaces, types, and functions by name. Also searches public
297
- members (properties, methods, accessors) of key framework classes, returning results in two sections:
298
- matching symbols and matching members with their owning class and role description.
309
+ Search for TypeScript classes, interfaces, types, and functions by name and JSDoc content.
310
+ Multi-word queries split into tokens matched with AND logic against the combined symbol name +
311
+ JSDoc text, so queries like `"panel modal"` find `ModalSupportModel` (which mentions Panel in
312
+ its JSDoc). Results are ranked with name matches above JSDoc-only matches. Also searches public
313
+ members (properties, methods, accessors) of key framework classes.
299
314
 
300
315
  | Parameter | Type | Required | Description |
301
316
  |-----------|------|----------|-------------|
302
- | `query` | string | Yes | Symbol or member name to search for (e.g. `"GridModel"`, `"lastLoadCompleted"`, `"setSortBy"`) |
317
+ | `query` | string | Yes | Search query - a symbol name (e.g. `"GridModel"`), keyword (e.g. `"tooltip"`), or multiple terms (e.g. `"panel modal"`, `"cube view store"`) |
303
318
  | `kind` | enum | No | Filter symbols by kind: `class`, `interface`, `type`, `function`, `const`, `enum`. Does not affect member results. |
304
319
  | `exported` | boolean | No | Exported symbols only. Default: `true` |
305
320
  | `limit` | number | No | Max symbol results, 1-50. Default: 20. Member results have a separate cap of 15. |
@@ -328,6 +343,10 @@ For classes that use the config-object constructor pattern (e.g. `GridModel`, `F
328
343
  the output includes a `Constructor:` line showing the config type name. This gives agents a natural
329
344
  follow-up: call `hoist-get-members` on the config interface to see available options.
330
345
 
346
+ When multiple exported symbols share the same name (e.g. `View` in both `cmp/viewmanager` and
347
+ `data/cube`), the output appends a disambiguation note listing alternates with their file paths.
348
+ Pass `filePath` to select a specific one. Dynamics stubs are excluded from the alternates list.
349
+
331
350
  | Parameter | Type | Required | Description |
332
351
  |-----------|------|----------|-------------|
333
352
  | `name` | string | Yes | Exact symbol name (e.g. `"GridModel"`) |
@@ -425,10 +444,12 @@ are needed.
425
444
 
426
445
  ### Doc Registry Entries
427
446
 
428
- **File:** `mcp/data/doc-registry.ts` (function `getRawEntries()`)
447
+ **File:** `docs/doc-registry.json`
429
448
 
430
- The doc registry is the single source of truth for all documentation that both the MCP server and
431
- CLI tools can search and serve. Each entry specifies an `id`, `title`, `file` path, `category`,
449
+ The doc registry is the single source of truth for all documentation that both the MCP server
450
+ and CLI tools can search and serve. It is a JSON file loaded at startup by
451
+ `mcp/data/doc-registry.ts`. Each entry in the `entries` array specifies an `id` (which doubles
452
+ as the file path relative to repo root), `title`, `mcpCategory`, `viewerCategory`,
432
453
  `description`, and `keywords` array.
433
454
 
434
455
  **When to update:**
@@ -438,9 +459,9 @@ CLI tools can search and serve. Each entry specifies an `id`, `title`, `file` pa
438
459
  - A documentation file is removed
439
460
  - The description or key topics for a doc change significantly
440
461
 
441
- **How to update:** Add, modify, or remove the corresponding `RawEntry` object in the
442
- `getRawEntries()` function. The `file` path is relative to the repo root. The `keywords` string
443
- is comma-separated and split automatically.
462
+ **How to update:** Add, modify, or remove the corresponding entry object in the `entries` array
463
+ of `docs/doc-registry.json`. The `id` field is the file path relative to the repo root (e.g.
464
+ `cmp/grid/README.md`). Keywords are a JSON array of strings.
444
465
 
445
466
  **Automated support:** The `xh-update-doc-links` Claude Code skill
446
467
  (`.claude/skills/xh-update-doc-links/`) includes a dedicated step that reconciles the doc registry
@@ -492,8 +513,8 @@ View, FormModel, BaseFieldModel, FieldModel, TabContainerModel
492
513
 
493
514
  | Change | Files to Update |
494
515
  |--------|----------------|
495
- | Add/rename/remove a documentation file | `mcp/data/doc-registry.ts`, `docs/README.md` |
496
- | Add upgrade notes for a new major version | `mcp/data/doc-registry.ts`, `docs/README.md` |
516
+ | Add/rename/remove a documentation file | `docs/doc-registry.json`, `docs/README.md` |
517
+ | Add upgrade notes for a new major version | `docs/doc-registry.json`, `docs/README.md` |
497
518
  | Add/rename/remove a top-level package | `mcp/data/ts-registry.ts` |
498
519
  | Add/rename/remove a member-indexed class | `mcp/data/ts-registry.ts` (constant `MEMBER_INDEXED_CLASSES`) |
499
520
 
@@ -561,22 +582,22 @@ server.registerResource(
561
582
 
562
583
  ### Adding a Doc Registry Entry
563
584
 
564
- Add a new `RawEntry` to the `getRawEntries()` function in `mcp/data/doc-registry.ts`:
585
+ Add a new entry to the `entries` array in `docs/doc-registry.json`:
565
586
 
566
- ```typescript
587
+ ```json
567
588
  {
568
- id: 'my-package',
569
- title: 'My Package',
570
- file: 'my-package/README.md', // Relative to repo root
571
- category: 'package',
572
- packageName: 'my-package',
573
- description: 'What this package does.',
574
- keywords: splitKeywords('keyword1, keyword2, keyword3')
575
- },
589
+ "id": "my-package/README.md",
590
+ "title": "My Package",
591
+ "mcpCategory": "package",
592
+ "viewerCategory": "components",
593
+ "description": "What this package does.",
594
+ "keywords": ["keyword1", "keyword2", "keyword3"]
595
+ }
576
596
  ```
577
597
 
578
- **Categories:** `package` (package READMEs), `concept` (cross-cutting docs), `devops` (build/deploy
579
- docs), `conventions` (AGENTS.md), `index` (docs/README.md).
598
+ **MCP categories:** `package`, `concept`, `devops`, `conventions`, `index`.
599
+ **Viewer categories:** `overview`, `concepts`, `core`, `components`, `desktop`, `mobile`,
600
+ `utilities`, `supporting`, `devops`, `upgrade`.
580
601
 
581
602
  ## Common Pitfalls
582
603
 
package/mcp/cli/ts.ts CHANGED
@@ -11,7 +11,8 @@ import {
11
11
  searchMembers,
12
12
  getSymbolDetail,
13
13
  getMembers,
14
- getCompanionSymbols
14
+ getCompanionSymbols,
15
+ findAlternateEntries
15
16
  } from '../data/ts-registry.js';
16
17
  import {formatSymbolSearch, formatSymbolDetail, formatMembers} from '../formatters/typescript.js';
17
18
 
@@ -44,6 +45,7 @@ Examples:
44
45
  hoist-ts search GridModel Search for symbols named GridModel
45
46
  hoist-ts search Store --kind class Search only classes
46
47
  hoist-ts search lastLoadCompleted Search symbols and class members
48
+ hoist-ts search "panel modal" Multi-word search (matches name + JSDoc)
47
49
  hoist-ts symbol GridModel Get full details for GridModel
48
50
  hoist-ts members GridModel List all GridModel properties and methods
49
51
  hoist-ts members Store List all Store members`
@@ -55,9 +57,12 @@ Examples:
55
57
  program
56
58
  .command('search')
57
59
  .description(
58
- 'Search for TypeScript symbols and class members by name. Searches both top-level symbols (classes, interfaces, types, functions) and public members of key framework classes.'
60
+ 'Search for TypeScript symbols and class members by name and JSDoc content. Multi-word queries match all terms against names and documentation. Also searches public members of key framework classes.'
61
+ )
62
+ .argument(
63
+ '<query>',
64
+ 'Search query — symbol name, keyword, or multiple terms (e.g. "panel modal")'
59
65
  )
60
- .argument('<query>', 'Symbol or member name to search for')
61
66
  .option(
62
67
  '-k, --kind <kind>',
63
68
  'Filter symbols by kind: class, interface, type, function, const, enum'
@@ -113,6 +118,20 @@ program
113
118
  text +=
114
119
  '\n\nTip: Use `hoist-ts members ' + name + '` to see all properties and methods.';
115
120
  }
121
+
122
+ if (!opts.file) {
123
+ const alternates = findAlternateEntries(name, detail.filePath);
124
+ if (alternates.length > 0) {
125
+ const altList = alternates
126
+ .map(
127
+ a =>
128
+ ` - [${a.kind}] ${a.sourcePackage} (${a.filePath.replace(/.*\/hoist-react\//, '')})`
129
+ )
130
+ .join('\n');
131
+ text += `\n\nNote: ${alternates.length + 1} symbols named "${name}" exist. Use --file to disambiguate:\n${altList}`;
132
+ }
133
+ }
134
+
116
135
  process.stdout.write(text + '\n');
117
136
  });
118
137
 
@@ -132,7 +151,23 @@ program
132
151
  console.error(formatMembers(result, name));
133
152
  process.exit(1);
134
153
  }
135
- process.stdout.write(formatMembers(result, name) + '\n');
154
+
155
+ let text = formatMembers(result, name);
156
+
157
+ if (!opts.file) {
158
+ const alternates = findAlternateEntries(name, result.symbol.filePath);
159
+ if (alternates.length > 0) {
160
+ const altList = alternates
161
+ .map(
162
+ a =>
163
+ ` - [${a.kind}] ${a.sourcePackage} (${a.filePath.replace(/.*\/hoist-react\//, '')})`
164
+ )
165
+ .join('\n');
166
+ text += `\n\nNote: ${alternates.length + 1} symbols named "${name}" exist. Use --file to disambiguate:\n${altList}`;
167
+ }
168
+ }
169
+
170
+ process.stdout.write(text + '\n');
136
171
  });
137
172
 
138
173
  program.parseAsync();
@@ -612,7 +612,10 @@ export async function ensureInitialized(): Promise<void> {
612
612
  /**
613
613
  * Search the symbol index by query string.
614
614
  *
615
- * Supports case-insensitive substring matching against symbol names.
615
+ * Supports case-insensitive matching against symbol names and JSDoc. Multi-word queries
616
+ * are split into tokens — all tokens must match (AND logic) against the combined name +
617
+ * JSDoc text. Results are scored: name matches rank above JSDoc-only matches.
618
+ *
616
619
  * Optionally filter by kind and/or export status.
617
620
  */
618
621
  export async function searchSymbols(
@@ -624,39 +627,51 @@ export async function searchSymbols(
624
627
  const queryLower = query.toLowerCase().trim();
625
628
  if (!queryLower) return [];
626
629
 
630
+ const tokens = queryLower.split(/\s+/);
627
631
  const limit = options?.limit ?? 50;
628
- const results: SymbolEntry[] = [];
632
+ const scored: {entry: SymbolEntry; nameMatches: number}[] = [];
629
633
 
630
634
  for (const [key, entries] of symbolIndex!) {
631
- if (!key.includes(queryLower)) continue;
632
-
633
635
  for (const entry of entries) {
634
636
  if (options?.kind && entry.kind !== options.kind) continue;
635
637
  if (options?.exported !== undefined && entry.isExported !== options.exported) continue;
636
- results.push(entry);
638
+
639
+ const jsDocLower = entry.jsDoc?.toLowerCase() ?? '';
640
+ const searchable = key + ' ' + jsDocLower;
641
+
642
+ // All tokens must match somewhere in the combined name + JSDoc text.
643
+ if (!tokens.every(t => searchable.includes(t))) continue;
644
+
645
+ // Count how many tokens matched the name specifically (for ranking).
646
+ const nameMatches = tokens.filter(t => key.includes(t)).length;
647
+ scored.push({entry, nameMatches});
637
648
  }
638
649
  }
639
650
 
640
- // Sort: exact matches first, then exported before non-exported, then alphabetically
641
- results.sort((a, b) => {
642
- const aExact = a.name.toLowerCase() === queryLower ? 0 : 1;
643
- const bExact = b.name.toLowerCase() === queryLower ? 0 : 1;
651
+ // Sort: most name-token matches first, then exact name match, then exported, then alpha.
652
+ scored.sort((a, b) => {
653
+ // Prefer entries where more tokens matched the name itself
654
+ if (a.nameMatches !== b.nameMatches) return b.nameMatches - a.nameMatches;
655
+
656
+ const aExact = a.entry.name.toLowerCase() === queryLower ? 0 : 1;
657
+ const bExact = b.entry.name.toLowerCase() === queryLower ? 0 : 1;
644
658
  if (aExact !== bExact) return aExact - bExact;
645
659
 
646
- const aExported = a.isExported ? 0 : 1;
647
- const bExported = b.isExported ? 0 : 1;
660
+ const aExported = a.entry.isExported ? 0 : 1;
661
+ const bExported = b.entry.isExported ? 0 : 1;
648
662
  if (aExported !== bExported) return aExported - bExported;
649
663
 
650
- return a.name.localeCompare(b.name);
664
+ return a.entry.name.localeCompare(b.entry.name);
651
665
  });
652
666
 
653
- return results.slice(0, limit);
667
+ return scored.slice(0, limit).map(s => s.entry);
654
668
  }
655
669
 
656
670
  /**
657
671
  * Search the member index by query string.
658
672
  *
659
- * Supports case-insensitive substring matching against member names.
673
+ * Supports case-insensitive substring matching against member names. Multi-word queries
674
+ * are split into tokens — all tokens must appear in the member name (AND logic).
660
675
  * Only searches members of classes in MEMBER_INDEXED_CLASSES.
661
676
  */
662
677
  export async function searchMembers(
@@ -668,11 +683,12 @@ export async function searchMembers(
668
683
  const queryLower = query.toLowerCase().trim();
669
684
  if (!queryLower) return [];
670
685
 
686
+ const tokens = queryLower.split(/\s+/);
671
687
  const limit = options?.limit ?? 15;
672
688
  const results: MemberIndexEntry[] = [];
673
689
 
674
690
  for (const [key, entries] of memberIndex!) {
675
- if (!key.includes(queryLower)) continue;
691
+ if (!tokens.every(t => key.includes(t))) continue;
676
692
  results.push(...entries);
677
693
  }
678
694
 
@@ -915,6 +931,24 @@ function extractInterfaceMembersWithInheritance(filePath: string, name: string):
915
931
  return allMembers;
916
932
  }
917
933
 
934
+ /**
935
+ * Find other exported symbols with the same name as the given entry, excluding
936
+ * dynamics stubs. Used to surface disambiguation hints when multiple real symbols
937
+ * share a name (e.g. `View` in both `cmp/viewmanager` and `data/cube`).
938
+ */
939
+ export function findAlternateEntries(name: string, selectedFilePath: string): SymbolEntry[] {
940
+ const key = name.toLowerCase();
941
+ const entries = symbolIndex!.get(key);
942
+ if (!entries) return [];
943
+ return entries.filter(
944
+ e =>
945
+ e.name === name &&
946
+ e.isExported &&
947
+ e.filePath !== selectedFilePath &&
948
+ !e.sourcePackage.startsWith('dynamics')
949
+ );
950
+ }
951
+
918
952
  /**
919
953
  * Find a symbol in the index by exact name.
920
954
  * Prefers exported symbols when multiple matches exist and no filePath filter.
@@ -935,8 +969,14 @@ function findIndexEntry(name: string, filePath?: string): SymbolEntry | null {
935
969
  return exact.find(e => e.filePath === resolved) ?? null;
936
970
  }
937
971
 
938
- // Prefer exported symbols
939
- return exact.find(e => e.isExported) ?? exact[0];
972
+ // Prefer exported symbols, and among those prefer class/interface over const/type stubs
973
+ // (e.g. dynamics/desktop.ts re-exports `export let Foo = null` alongside the real class).
974
+ const exported = exact.filter(e => e.isExported);
975
+ if (exported.length > 1) {
976
+ const preferred = exported.find(e => e.kind === 'class' || e.kind === 'interface');
977
+ if (preferred) return preferred;
978
+ }
979
+ return exported[0] ?? exact[0];
940
980
  }
941
981
 
942
982
  /**
@@ -13,7 +13,8 @@ import {
13
13
  searchMembers,
14
14
  getSymbolDetail,
15
15
  getMembers,
16
- getCompanionSymbols
16
+ getCompanionSymbols,
17
+ findAlternateEntries
17
18
  } from '../data/ts-registry.js';
18
19
  import {formatSymbolSearch, formatSymbolDetail, formatMembers} from '../formatters/typescript.js';
19
20
 
@@ -33,12 +34,12 @@ export function registerTsTools(server: McpServer): void {
33
34
  {
34
35
  title: 'Search Hoist TypeScript Symbols',
35
36
  description:
36
- 'Search for TypeScript classes, interfaces, types, and functions across the hoist-react framework by name. Also searches public members (properties, methods, accessors) of key framework classes like HoistModel, GridModel, Store, and others. Returns matching symbols and members with their kind, source, and context.',
37
+ 'Search for TypeScript classes, interfaces, types, and functions across the hoist-react framework by name and JSDoc content. Multi-word queries match all terms (AND logic) against symbol names and their documentation e.g. "panel modal" finds ModalSupportModel via its JSDoc. Also searches public members of key framework classes. Results are ranked: name matches above JSDoc-only matches.',
37
38
  inputSchema: z.object({
38
39
  query: z
39
40
  .string()
40
41
  .describe(
41
- 'Symbol or member name to search for (e.g. "GridModel", "Store", "lastLoadCompleted", "setSortBy")'
42
+ 'Search query a symbol name (e.g. "GridModel"), a keyword (e.g. "tooltip"), or multiple terms (e.g. "panel modal", "cube view store")'
42
43
  ),
43
44
  kind: z
44
45
  .enum(['class', 'interface', 'type', 'function', 'const', 'enum'])
@@ -113,6 +114,19 @@ export function registerTsTools(server: McpServer): void {
113
114
  text += '\n\nUse hoist-get-members to see all properties and methods.';
114
115
  }
115
116
 
117
+ if (detail && !filePath) {
118
+ const alternates = findAlternateEntries(name, detail.filePath);
119
+ if (alternates.length > 0) {
120
+ const altList = alternates
121
+ .map(
122
+ a =>
123
+ ` - [${a.kind}] ${a.sourcePackage} (${a.filePath.replace(/.*\/hoist-react\//, '')})`
124
+ )
125
+ .join('\n');
126
+ text += `\n\nNote: ${alternates.length + 1} symbols named "${name}" exist. Pass filePath to disambiguate:\n${altList}`;
127
+ }
128
+ }
129
+
116
130
  return {content: [{type: 'text' as const, text}]};
117
131
  }
118
132
  );
@@ -146,7 +160,21 @@ export function registerTsTools(server: McpServer): void {
146
160
  },
147
161
  async ({name, filePath}) => {
148
162
  const result = await getMembers(name, filePath);
149
- const text = formatMembers(result, name);
163
+ let text = formatMembers(result, name);
164
+
165
+ if (result && !filePath) {
166
+ const alternates = findAlternateEntries(name, result.symbol.filePath);
167
+ if (alternates.length > 0) {
168
+ const altList = alternates
169
+ .map(
170
+ a =>
171
+ ` - [${a.kind}] ${a.sourcePackage} (${a.filePath.replace(/.*\/hoist-react\//, '')})`
172
+ )
173
+ .join('\n');
174
+ text += `\n\nNote: ${alternates.length + 1} symbols named "${name}" exist. Pass filePath to disambiguate:\n${altList}`;
175
+ }
176
+ }
177
+
150
178
  return {content: [{type: 'text' as const, text}]};
151
179
  }
152
180
  );
@@ -28,12 +28,14 @@ export const aboutDialog = hoistCmp.factory({
28
28
  icon: Icon.info(),
29
29
  title: `About ${XH.appName}`,
30
30
  className: 'xh-about-dialog',
31
+ testId: 'xh-about-dialog',
31
32
  item: model.getTable(),
32
33
  isOpen: model.isOpen,
33
34
  bbar: [
34
35
  button({
35
36
  text: 'Send Client Health Report',
36
37
  icon: Icon.health(),
38
+ testId: 'xh-about-health-report-btn',
37
39
  omit: !XH.clientHealthService.enabled,
38
40
  onClick: async () => {
39
41
  try {
@@ -51,6 +53,7 @@ export const aboutDialog = hoistCmp.factory({
51
53
  button({
52
54
  text: 'Close',
53
55
  outlined: true,
56
+ testId: 'xh-about-close-btn',
54
57
  onClick: () => model.hide()
55
58
  })
56
59
  ]
@@ -61,6 +61,7 @@ const actionButton = hoistCmp.factory<BannerModel>(({model}) => {
61
61
  return button({
62
62
  className: 'xh-banner__action-button',
63
63
  outlined: true,
64
+ testId: `xh-banner-${model.category}-action-btn`,
64
65
  ...actionButtonProps
65
66
  });
66
67
  });
@@ -73,6 +74,7 @@ const dismissButton = hoistCmp.factory<BannerModel>(({model}) => {
73
74
  icon: Icon.close(),
74
75
  minimal: true,
75
76
  className: 'xh-banner__dismiss-button',
77
+ testId: `xh-banner-${category}-dismiss-btn`,
76
78
  onClick: () => {
77
79
  XH.hideBanner(category);
78
80
  if (isFunction(onClose)) onClose(model);
@@ -47,6 +47,7 @@ export const exceptionDialog = hoistCmp.factory({
47
47
  button({
48
48
  icon: Icon.search(),
49
49
  text: 'Details',
50
+ testId: 'xh-exception-details-btn',
50
51
  onClick: () => model.openDetails(),
51
52
  omit: !options.showAsError
52
53
  }),
@@ -54,6 +55,7 @@ export const exceptionDialog = hoistCmp.factory({
54
55
  omit: !identityService?.isImpersonating,
55
56
  text: 'End Impers',
56
57
  minimal: true,
58
+ testId: 'xh-exception-end-impersonation-btn',
57
59
  onClick: () => identityService.endImpersonateAsync()
58
60
  }),
59
61
  filler(),
@@ -74,10 +76,12 @@ export const dismissButton = hoistCmp.factory<ExceptionDialogModel>(({model}) =>
74
76
  ? button({
75
77
  icon: Icon.refresh(),
76
78
  text: 'Reload App',
79
+ testId: 'xh-exception-dismiss-btn',
77
80
  onClick: () => XH.reloadApp()
78
81
  })
79
82
  : button({
80
83
  text: 'Close',
84
+ testId: 'xh-exception-dismiss-btn',
81
85
  onClick: () => model.close()
82
86
  });
83
87
  });
@@ -51,6 +51,7 @@ export const exceptionDialogDetails = hoistCmp.factory({
51
51
  button({
52
52
  icon: Icon.envelope(),
53
53
  text: 'Send Message',
54
+ testId: 'xh-exception-details-send-btn',
54
55
  onClick: () => {
55
56
  XH.prompt<string>({
56
57
  title: 'Send Message',
@@ -31,13 +31,15 @@ export const feedbackDialog = hoistCmp.factory({
31
31
  content: textArea({
32
32
  placeholder: 'Please enter your comments...',
33
33
  bind: 'message',
34
- height: 250
34
+ height: 250,
35
+ testId: 'xh-feedback-message'
35
36
  }),
36
37
  buttons: [
37
38
  filler(),
38
39
  button({
39
40
  text: 'Cancel',
40
41
  minimal: true,
42
+ testId: 'xh-feedback-cancel-btn',
41
43
  onClick: () => model.hide()
42
44
  }),
43
45
  button({
@@ -45,6 +47,7 @@ export const feedbackDialog = hoistCmp.factory({
45
47
  icon: Icon.mail(),
46
48
  intent: 'primary',
47
49
  outlined: true,
50
+ testId: 'xh-feedback-send-btn',
48
51
  onClick: () => model.submitAsync()
49
52
  })
50
53
  ]