@xh/hoist 59.3.1 → 59.4.0

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 (54) hide show
  1. package/CHANGELOG.md +30 -2
  2. package/admin/differ/DifferModel.ts +5 -7
  3. package/appcontainer/AppContainerModel.ts +8 -10
  4. package/appcontainer/AppStateModel.ts +3 -2
  5. package/appcontainer/PageStateModel.ts +1 -2
  6. package/appcontainer/SizingModeModel.ts +2 -2
  7. package/cmp/ag-grid/AgGrid.ts +4 -3
  8. package/cmp/ag-grid/AgGridModel.ts +8 -9
  9. package/cmp/chart/Chart.ts +4 -3
  10. package/cmp/dataview/DataViewModel.ts +2 -2
  11. package/cmp/filter/FilterChooserModel.ts +5 -5
  12. package/cmp/grid/Grid.ts +2 -2
  13. package/cmp/grid/GridContextMenu.ts +2 -2
  14. package/cmp/grid/GridModel.ts +13 -21
  15. package/cmp/grid/Types.ts +6 -5
  16. package/cmp/grid/columns/Column.ts +12 -12
  17. package/cmp/grid/columns/ColumnGroup.ts +17 -6
  18. package/cmp/grid/helpers/GridCountLabel.ts +5 -4
  19. package/cmp/grid/impl/ColumnWidthCalculator.ts +3 -3
  20. package/cmp/grid/impl/GridPersistenceModel.ts +11 -4
  21. package/cmp/grid/renderers/MultiFieldRenderer.ts +28 -22
  22. package/cmp/grouping/GroupingChooserModel.ts +2 -2
  23. package/cmp/tab/TabContainerModel.ts +2 -2
  24. package/cmp/zoneGrid/ZoneGridModel.ts +52 -36
  25. package/cmp/zoneGrid/impl/ZoneGridPersistenceModel.ts +10 -4
  26. package/core/HoistBase.ts +44 -5
  27. package/core/XH.ts +3 -3
  28. package/core/elem.ts +2 -2
  29. package/core/impl/InstallServices.ts +2 -8
  30. package/core/load/LoadSupport.ts +3 -3
  31. package/core/model/HoistModel.ts +1 -1
  32. package/data/Store.ts +1 -1
  33. package/data/UrlStore.ts +3 -3
  34. package/data/filter/CompoundFilter.ts +5 -3
  35. package/data/filter/FieldFilter.ts +4 -3
  36. package/data/filter/Filter.ts +2 -3
  37. package/data/filter/FunctionFilter.ts +2 -1
  38. package/data/impl/RecordSet.ts +5 -5
  39. package/desktop/appcontainer/ToastSource.ts +1 -1
  40. package/desktop/cmp/rest/Actions.ts +15 -9
  41. package/desktop/cmp/treemap/TreeMap.ts +4 -8
  42. package/package.json +1 -1
  43. package/svc/AutoRefreshService.ts +3 -3
  44. package/svc/EnvironmentService.ts +1 -1
  45. package/svc/FetchService.ts +5 -5
  46. package/svc/GridAutosizeService.ts +4 -7
  47. package/svc/TrackService.ts +6 -6
  48. package/svc/WebSocketService.ts +14 -15
  49. package/utils/async/AsyncUtils.ts +3 -2
  50. package/utils/async/Timer.ts +32 -19
  51. package/utils/js/BrowserUtils.ts +8 -8
  52. package/utils/js/LangUtils.ts +10 -9
  53. package/utils/js/LogUtils.ts +66 -26
  54. package/utils/react/LayoutPropUtils.ts +3 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## 59.4.0 - 2023-11-28
4
+
5
+ ### 💥 Breaking Changes
6
+
7
+ * The constructors for `ColumnGroup` no long accept arbitrary rest (e.g `...rest`)
8
+ arguments for applying app-specific data to the object. Instead, use the new `appData` property.
9
+
10
+ ### ⚙️ Technical
11
+
12
+ * Enhanced `LogUtils` to support logging objects (and any other non-string values). Also
13
+ added new exports for `logWarn()` and `logError()` with the same standardized formatting.
14
+ * Added standardized `LogUtils` methods to `HoistBase`, for use within Hoist models and services.
15
+
16
+ ### 🐞 Bug Fixes
17
+
18
+ * `ZoneGrid` will no longer render labels or delimiters for empty values.
19
+
20
+ ### ⚙️ Typescript API Adjustments
21
+
22
+ * Updated type for `ReactionSpec.equals` to include already-supported string shorthands.
23
+
24
+ ## 59.3.2 - 2023-11-21
25
+
26
+ ### 🐞 Bug Fixes
27
+
28
+ * `ZoneGrid` will more gracefully handle state that has become out of sync with its mapper
29
+ requirements.
30
+
3
31
  ## 59.3.1 - 2023-11-10
4
32
 
5
33
  ### 🐞 Bug Fixes
@@ -49,9 +77,9 @@
49
77
  * Improved core `HoistComponent` performance by preventing unnecessary re-renderings triggered by
50
78
  spurious model lookup changes.
51
79
  * New flag `GridModel.experimental.enableFullWidthScroll` enables scrollbars to span pinned columns.
52
- * Early test release behind the flag, expected to made the default behavior in next release.
80
+ * Early test release behind the flag, expected to made the default behavior in next release.
53
81
  * Renamed `XH.getActiveModels()` to `XH.getModels()` for clarity / consistency.
54
- * API change, but not expected to impact applications.
82
+ * API change, but not expected to impact applications.
55
83
  * Added `XH.getModel()` convenience method to return the first matching model.
56
84
 
57
85
  ## 59.2.0 - 2023-10-16
@@ -6,18 +6,16 @@
6
6
  */
7
7
 
8
8
  import {GridModel} from '@xh/hoist/cmp/grid';
9
+ import {div, hbox, p} from '@xh/hoist/cmp/layout';
9
10
  import {HoistModel, LoadSpec, managed, XH} from '@xh/hoist/core';
11
+ import {RecordActionSpec} from '@xh/hoist/data';
10
12
  import {actionCol} from '@xh/hoist/desktop/cmp/grid';
11
- import {div, p} from '@xh/hoist/cmp/layout';
12
13
  import {Icon} from '@xh/hoist/icon';
13
- import {bindable, makeObservable, observable, action} from '@xh/hoist/mobx';
14
+ import {action, bindable, makeObservable, observable} from '@xh/hoist/mobx';
14
15
  import {pluralize} from '@xh/hoist/utils/js';
15
- import {hbox} from '@xh/hoist/cmp/layout';
16
- import {cloneDeep, isEqual, isString, isNil, omit, remove, trimEnd} from 'lodash';
16
+ import {cloneDeep, isEqual, isNil, isString, omit, remove, trimEnd} from 'lodash';
17
17
  import {hspacer} from '../../cmp/layout';
18
-
19
18
  import {DifferDetailModel} from './DifferDetailModel';
20
- import {RecordActionSpec} from '@xh/hoist/data';
21
19
 
22
20
  /**
23
21
  * @internal
@@ -378,7 +376,7 @@ export class DifferModel extends HoistModel {
378
376
  content = await window.navigator.clipboard.readText();
379
377
  content = JSON.parse(content);
380
378
  } catch (e) {
381
- console.warn('Error reading config from clipboard', e);
379
+ this.logWarn('Error reading config from clipboard', e);
382
380
  }
383
381
 
384
382
  this.clipboardContent = content;
@@ -114,8 +114,8 @@ export class AppContainerModel extends HoistModel {
114
114
  }
115
115
 
116
116
  /**
117
- * Called when application container first mounted in order to trigger initial
118
- * authentication and initialization of framework and application.
117
+ * Called when {@link AppContainer} first mounted.
118
+ * Triggers initial authentication and initialization of Hoist and application.
119
119
  */
120
120
  async initAsync() {
121
121
  // Avoid multiple calls, which can occur if AppContainer remounted.
@@ -196,14 +196,12 @@ export class AppContainerModel extends HoistModel {
196
196
  numbro['BigNumber'].clone();
197
197
  }
198
198
 
199
- // Confirm hoist-core version after environment service loaded
200
- const hcVersion = XH.environmentService.get('hoistCoreVersion');
201
- if (!checkMinVersion(hcVersion, MIN_HOIST_CORE_VERSION)) {
202
- throw XH.exception(`
203
- This version of Hoist React requires the server to run Hoist Core
204
- v${MIN_HOIST_CORE_VERSION} or greater. Version ${hcVersion} detected.
205
- `);
206
- }
199
+ // Confirm hoist-core version after environment service loaded.
200
+ const hcVersion = XH.getEnv('hoistCoreVersion');
201
+ throwIf(
202
+ !checkMinVersion(hcVersion, MIN_HOIST_CORE_VERSION),
203
+ `This version of Hoist React requires the server to run Hoist Core ≥ v${MIN_HOIST_CORE_VERSION}. Version ${hcVersion} detected.`
204
+ );
207
205
 
208
206
  await installServicesAsync([
209
207
  AlertBannerService,
@@ -7,7 +7,7 @@
7
7
  import {AppState, AppSuspendData, HoistModel, XH} from '@xh/hoist/core';
8
8
  import {action, makeObservable, observable, reaction} from '@xh/hoist/mobx';
9
9
  import {Timer} from '@xh/hoist/utils/async';
10
- import {getClientDeviceInfo, logDebug} from '@xh/hoist/utils/js';
10
+ import {getClientDeviceInfo} from '@xh/hoist/utils/js';
11
11
  import {isBoolean, isString} from 'lodash';
12
12
 
13
13
  /**
@@ -34,7 +34,7 @@ export class AppStateModel extends HoistModel {
34
34
  @action
35
35
  setAppState(nextState: AppState) {
36
36
  if (this.state !== nextState) {
37
- logDebug(`AppState change: ${this.state} → ${nextState}`, this);
37
+ this.logDebug(`AppState change: ${this.state} → ${nextState}`);
38
38
  }
39
39
  this.state = nextState;
40
40
  }
@@ -87,6 +87,7 @@ export class AppStateModel extends HoistModel {
87
87
  appVersion: XH.appVersion,
88
88
  appBuild: XH.appBuild,
89
89
  locationHref: window.location.href,
90
+ wasDiscarded: window.document['wasDiscarded'],
90
91
  ...getClientDeviceInfo()
91
92
  },
92
93
  logData: ['appVersion', 'appBuild'],
@@ -6,7 +6,6 @@
6
6
  */
7
7
  import {HoistModel, PageState} from '@xh/hoist/core';
8
8
  import {action, makeObservable, observable} from '@xh/hoist/mobx';
9
- import {logDebug} from '@xh/hoist/utils/js';
10
9
 
11
10
  /**
12
11
  * Implementation of PageState maintenance.
@@ -35,7 +34,7 @@ export class PageStateModel extends HoistModel {
35
34
  private setState(nextState: PageState) {
36
35
  if (this.state == 'terminated' || this.state == nextState) return;
37
36
 
38
- logDebug(`PageState change: ${this.state} → ${nextState}`, this);
37
+ this.logDebug(`PageState change: ${this.state} → ${nextState}`);
39
38
  this.state = nextState;
40
39
  }
41
40
 
@@ -40,7 +40,7 @@ export class SizingModeModel extends HoistModel {
40
40
  platform = this.getPlatform();
41
41
 
42
42
  if (!isPlainObject(pref)) {
43
- console.warn(
43
+ this.logWarn(
44
44
  `Required pref 'xhSizingMode' must be type JSON - update via Admin Console.`
45
45
  );
46
46
  return;
@@ -48,7 +48,7 @@ export class SizingModeModel extends HoistModel {
48
48
 
49
49
  XH.setPref('xhSizingMode', {...pref, [platform]: sizingMode});
50
50
  } else {
51
- console.warn(`Missing required JSON pref 'xhSizingMode' - add via Admin Console.`);
51
+ this.logWarn(`Missing required JSON pref 'xhSizingMode' - add via Admin Console.`);
52
52
  }
53
53
  }
54
54
 
@@ -18,6 +18,7 @@ import {
18
18
  XH
19
19
  } from '@xh/hoist/core';
20
20
  import {AgGridReact, GridOptions} from '@xh/hoist/kit/ag-grid';
21
+ import {logError} from '@xh/hoist/utils/js';
21
22
  import {splitLayoutProps} from '@xh/hoist/utils/react';
22
23
  import classNames from 'classnames';
23
24
  import {isNil} from 'lodash';
@@ -60,9 +61,9 @@ export const [AgGrid, agGrid] = hoistCmp.withFactory<AgGridProps>({
60
61
 
61
62
  render({model, className, testId, ...props}, ref) {
62
63
  if (!AgGridReact) {
63
- console.error(
64
- 'ag-Grid has not been imported in to this application. Please import and ' +
65
- 'register modules in Bootstrap.js. See the XH Toolbox app for an example.'
64
+ logError(
65
+ 'AG Grid not imported/licensed by this app - import and register modules in Bootstrap.ts. See the XH Toolbox app for an example.',
66
+ AgGrid
66
67
  );
67
68
  return placeholder('ag-Grid library not available.');
68
69
  }
@@ -5,6 +5,7 @@
5
5
  * Copyright © 2023 Extremely Heavy Industries Inc.
6
6
  */
7
7
  import {HoistModel, PlainObject, SizingMode, Some} from '@xh/hoist/core';
8
+ import type {ColumnApi, GridApi, IRowNode, SortDirection} from '@xh/hoist/kit/ag-grid';
8
9
  import {action, bindable, computed, makeObservable, observable} from '@xh/hoist/mobx';
9
10
  import {throwIf} from '@xh/hoist/utils/js';
10
11
  import {
@@ -23,8 +24,6 @@ import {
23
24
  } from 'lodash';
24
25
  import {GridSorter, GridSorterLike} from '../grid/GridSorter';
25
26
 
26
- import type {ColumnApi, GridApi, SortDirection} from '@xh/hoist/kit/ag-grid';
27
-
28
27
  export interface AgGridModelConfig {
29
28
  sizingMode?: SizingMode;
30
29
 
@@ -177,7 +176,7 @@ export class AgGridModel extends HoistModel {
177
176
  try {
178
177
  return this[`get${startCase(type)}State`]();
179
178
  } catch (err) {
180
- console.warn(`Encountered errors retrieving ${type} state:`, err);
179
+ this.logWarn(`Encountered errors retrieving ${type} state`, err);
181
180
  errors[type] = err.toString();
182
181
  }
183
182
  };
@@ -358,7 +357,7 @@ export class AgGridModel extends HoistModel {
358
357
  col.setSort(state.sort);
359
358
  col.setSortIndex(state.sortIndex);
360
359
  } else {
361
- console.warn(
360
+ this.logWarn(
362
361
  'Could not find a secondary column to associate with the pivot column path',
363
362
  state.colId
364
363
  );
@@ -554,7 +553,7 @@ export class AgGridModel extends HoistModel {
554
553
  * @returns the first row in the grid, after sorting and filtering, which
555
554
  * has data associated with it (i.e. not a group or other synthetic row).
556
555
  */
557
- getFirstSelectableRowNode(): PlainObject {
556
+ getFirstSelectableRowNode(): IRowNode {
558
557
  this.throwIfNotReady();
559
558
 
560
559
  let ret = null;
@@ -595,7 +594,7 @@ export class AgGridModel extends HoistModel {
595
594
  /**
596
595
  * @returns row data pinned to the bottom of the grid
597
596
  */
598
- getPinnedBottomRowData() {
597
+ getPinnedBottomRowData(): PlainObject[] {
599
598
  this.throwIfNotReady();
600
599
  return this.getPinnedRowData('Bottom');
601
600
  }
@@ -605,7 +604,7 @@ export class AgGridModel extends HoistModel {
605
604
  //------------------------
606
605
  @action
607
606
  handleGridReady({api, columnApi}) {
608
- console.debug(`AgGridModel ${this.xhId} initializing`);
607
+ this.logDebug(`Initializing`, this.xhId);
609
608
  throwIf(
610
609
  this.agApi && this.agApi != api,
611
610
  'Attempted to mount a grid on a GridModel that is already in use. ' +
@@ -617,12 +616,12 @@ export class AgGridModel extends HoistModel {
617
616
 
618
617
  @action
619
618
  handleGridUnmount() {
620
- console.debug(`AgGridModel ${this.xhId} un-initializing`);
619
+ this.logDebug(`Un-initializing`, this.xhId);
621
620
  this.agApi = null;
622
621
  this.agColumnApi = null;
623
622
  }
624
623
 
625
- private getPinnedRowData(side) {
624
+ private getPinnedRowData(side: string): PlainObject[] {
626
625
  const {agApi} = this,
627
626
  count = agApi[`getPinned${side}RowCount`](),
628
627
  ret = [];
@@ -22,6 +22,7 @@ import {useContextMenu} from '@xh/hoist/dynamics/desktop';
22
22
  import {Icon} from '@xh/hoist/icon';
23
23
  import {Highcharts} from '@xh/hoist/kit/highcharts';
24
24
  import {runInAction} from '@xh/hoist/mobx';
25
+ import {logError} from '@xh/hoist/utils/js';
25
26
  import {
26
27
  createObservableRef,
27
28
  getLayoutProps,
@@ -61,9 +62,9 @@ export const [Chart, chart] = hoistCmp.withFactory<ChartProps>({
61
62
 
62
63
  render({model, className, aspectRatio, testId, ...props}, ref) {
63
64
  if (!Highcharts) {
64
- console.error(
65
- 'Highcharts has not been imported in to this application. Please import and ' +
66
- 'register in Bootstrap.js. See the XH Toolbox app for an example.'
65
+ logError(
66
+ 'Highcharts not imported by this app - import and register modules in Bootstrap.ts. See the XH Toolbox app for an example.',
67
+ Chart
67
68
  );
68
69
  return placeholder('Highcharts library not available.');
69
70
  }
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Copyright © 2023 Extremely Heavy Industries Inc.
6
6
  */
7
- import {RowClickedEvent, RowDoubleClickedEvent} from '@ag-grid-community/core';
7
+ import {RowClickedEvent, RowDoubleClickedEvent, RowHeightParams} from '@ag-grid-community/core';
8
8
  import {
9
9
  ColumnRenderer,
10
10
  ColumnSpec,
@@ -127,7 +127,7 @@ export interface DataViewConfig {
127
127
  export type ItemHeightFn = (params: {
128
128
  record: StoreRecord;
129
129
  dataViewModel: DataViewModel;
130
- agParams: PlainObject;
130
+ agParams: RowHeightParams;
131
131
  }) => number;
132
132
 
133
133
  /**
@@ -199,7 +199,7 @@ export class FilterChooserModel extends HoistModel {
199
199
  run: state => this.provider.write(state)
200
200
  });
201
201
  } catch (e) {
202
- console.error(e);
202
+ this.logError(e);
203
203
  XH.safeDestroy(this.provider);
204
204
  this.provider = null;
205
205
  }
@@ -251,7 +251,7 @@ export class FilterChooserModel extends HoistModel {
251
251
  }
252
252
 
253
253
  // 2) Main path - set internal value.
254
- console.debug('Setting FilterChooser value:', value);
254
+ this.logDebug('Setting value', value);
255
255
  this.value = value;
256
256
 
257
257
  // 3) Set props on select input needed to display
@@ -292,7 +292,7 @@ export class FilterChooserModel extends HoistModel {
292
292
  })
293
293
  .linkTo(this.filterTask);
294
294
  } catch (e) {
295
- console.error('Failed to set value on FilterChooserModel', e);
295
+ this.logError('Failed to set value', e);
296
296
  this.value = null;
297
297
  this.selectOptions = null;
298
298
  this.selectValue = null;
@@ -490,7 +490,7 @@ export class FilterChooserModel extends HoistModel {
490
490
  if (f === null) return true;
491
491
  if (f instanceof FieldFilter) {
492
492
  if (!this.getFieldSpec(f.field)) {
493
- console.error(`Invalid Filter for FilterChooser: ${f.field}`);
493
+ this.logError(`Invalid FieldFilter - no fieldSpec configured for ${f.field}.`);
494
494
  return false;
495
495
  }
496
496
  return true;
@@ -500,7 +500,7 @@ export class FilterChooserModel extends HoistModel {
500
500
  return f.filters.every(it => this.validateFilter(it));
501
501
  }
502
502
 
503
- console.error('Invalid filter for FilterChooser', f);
503
+ this.logError('Invalid Filter - unhandled type', f);
504
504
  return false;
505
505
  }
506
506
 
package/cmp/grid/Grid.ts CHANGED
@@ -41,7 +41,7 @@ import type {
41
41
  } from '@xh/hoist/kit/ag-grid';
42
42
  import {computed, observer} from '@xh/hoist/mobx';
43
43
  import {wait} from '@xh/hoist/promise';
44
- import {consumeEvent, isDisplayed, logDebug, logWithDebug} from '@xh/hoist/utils/js';
44
+ import {consumeEvent, isDisplayed, logWithDebug} from '@xh/hoist/utils/js';
45
45
  import {createObservableRef, getLayoutProps} from '@xh/hoist/utils/react';
46
46
  import classNames from 'classnames';
47
47
  import {debounce, isEmpty, isEqual, isNil, max, maxBy, merge} from 'lodash';
@@ -634,7 +634,7 @@ export class GridLocalModel extends HoistModel {
634
634
  let transaction = null;
635
635
  if (prevCount !== 0) {
636
636
  transaction = this.genTransaction(newRs, prevRs);
637
- logDebug(this.transactionLogStr(transaction), this);
637
+ this.logDebug(this.transactionLogStr(transaction));
638
638
 
639
639
  if (!this.transactionIsEmpty(transaction)) {
640
640
  agApi.applyTransaction(transaction);
@@ -4,9 +4,9 @@
4
4
  *
5
5
  * Copyright © 2023 Extremely Heavy Industries Inc.
6
6
  */
7
- import {PlainObject} from '@xh/hoist/core';
8
7
  import {GridModel} from '@xh/hoist/cmp/grid';
9
8
  import {RecordActionLike} from '@xh/hoist/data';
9
+ import {GetContextMenuItemsParams} from '@xh/hoist/kit/ag-grid';
10
10
 
11
11
  /**
12
12
  * If a String, value can be '-' for a separator, or a token supported by ag-Grid
@@ -46,4 +46,4 @@ export type GridContextMenuToken =
46
46
  */
47
47
  export type GridContextMenuSpec =
48
48
  | GridContextMenuItemLike[]
49
- | ((agParams: PlainObject, gridModel: GridModel) => GridContextMenuItemLike[]);
49
+ | ((agParams: GetContextMenuItemsParams, gridModel: GridModel) => GridContextMenuItemLike[]);
@@ -56,14 +56,7 @@ import {action, bindable, makeObservable, observable, when} from '@xh/hoist/mobx
56
56
  import {wait, waitFor} from '@xh/hoist/promise';
57
57
  import {ExportOptions} from '@xh/hoist/svc/GridExportService';
58
58
  import {SECONDS} from '@xh/hoist/utils/datetime';
59
- import {
60
- deepFreeze,
61
- logWithDebug,
62
- throwIf,
63
- warnIf,
64
- withDebug,
65
- withDefault
66
- } from '@xh/hoist/utils/js';
59
+ import {deepFreeze, logWithDebug, throwIf, warnIf, withDefault} from '@xh/hoist/utils/js';
67
60
  import equal from 'fast-deep-equal';
68
61
  import {
69
62
  castArray,
@@ -818,7 +811,7 @@ export class GridModel extends HoistModel {
818
811
 
819
812
  const indexCount = indices.length;
820
813
  if (indexCount !== records.length) {
821
- console.warn('Grid row nodes not found for all provided records.');
814
+ this.logWarn('Grid row nodes not found for all provided records.');
822
815
  }
823
816
 
824
817
  if (indexCount === 1) {
@@ -978,7 +971,7 @@ export class GridModel extends HoistModel {
978
971
 
979
972
  const invalidColIds = colIds.filter(it => !this.findColumn(this.columns, it));
980
973
  if (invalidColIds.length) {
981
- console.warn(
974
+ this.logWarn(
982
975
  'Unknown colId specified in groupBy - grid will not be grouped.',
983
976
  invalidColIds
984
977
  );
@@ -1025,7 +1018,7 @@ export class GridModel extends HoistModel {
1025
1018
  it => !it.colId?.startsWith('ag-Grid') && !this.findColumn(this.columns, it.colId)
1026
1019
  );
1027
1020
  if (invalidSorters.length) {
1028
- console.warn('GridSorter colId not found in grid columns', invalidSorters);
1021
+ this.logWarn('GridSorter colId not found in grid columns', invalidSorters);
1029
1022
  return;
1030
1023
  }
1031
1024
 
@@ -1360,10 +1353,9 @@ export class GridModel extends HoistModel {
1360
1353
 
1361
1354
  const rowIndex = agApi.getRowNode(recToEdit?.agId)?.rowIndex;
1362
1355
  if (isNil(rowIndex) || rowIndex < 0) {
1363
- console.warn(
1364
- 'Unable to start editing - ' + record
1365
- ? 'specified record not found'
1366
- : 'no records found'
1356
+ this.logWarn(
1357
+ 'Unable to start editing',
1358
+ record ? 'specified record not found' : 'no records found'
1367
1359
  );
1368
1360
  return;
1369
1361
  }
@@ -1381,11 +1373,11 @@ export class GridModel extends HoistModel {
1381
1373
  }
1382
1374
 
1383
1375
  if (!colToEdit) {
1384
- console.warn(
1385
- 'Unable to start editing - ' +
1386
- (colId
1387
- ? `column with colId ${colId} not found, or not editable`
1388
- : 'no editable columns found')
1376
+ this.logWarn(
1377
+ 'Unable to start editing',
1378
+ colId
1379
+ ? `column with colId ${colId} not found, or not editable`
1380
+ : 'no editable columns found'
1389
1381
  );
1390
1382
  return;
1391
1383
  }
@@ -1438,7 +1430,7 @@ export class GridModel extends HoistModel {
1438
1430
  try {
1439
1431
  await when(() => this.isReady, {timeout});
1440
1432
  } catch (ignored) {
1441
- withDebug(`Grid failed to enter ready state after waiting ${timeout}ms`, null, this);
1433
+ this.logDebug(`Grid failed to enter ready state after waiting ${timeout}ms`);
1442
1434
  }
1443
1435
  await wait();
1444
1436
 
package/cmp/grid/Types.ts CHANGED
@@ -5,8 +5,9 @@
5
5
  * Copyright © 2023 Extremely Heavy Industries Inc.
6
6
  */
7
7
 
8
+ import {IRowNode} from '@xh/hoist/kit/ag-grid';
8
9
  import {GridFilterFieldSpecConfig} from '@xh/hoist/cmp/grid/filter/GridFilterFieldSpec';
9
- import {HSide, PersistOptions, PlainObject, SizingMode, Some} from '@xh/hoist/core';
10
+ import {HSide, PersistOptions, SizingMode, Some} from '@xh/hoist/core';
10
11
  import {Store, StoreRecord, View} from '@xh/hoist/data';
11
12
  import {ReactElement, ReactNode} from 'react';
12
13
  import {Column} from './columns/Column';
@@ -54,8 +55,8 @@ export type GridGroupSortFn = (
54
55
  groupField: string,
55
56
  metadata: {
56
57
  gridModel: GridModel;
57
- nodeA: PlainObject;
58
- nodeB: PlainObject;
58
+ nodeA: IRowNode;
59
+ nodeB: IRowNode;
59
60
  }
60
61
  ) => number;
61
62
 
@@ -167,8 +168,8 @@ export type ColumnComparator<T = any> = (
167
168
  params: {
168
169
  recordA: StoreRecord;
169
170
  recordB: StoreRecord;
170
- agNodeA: PlainObject;
171
- agNodeB: PlainObject;
171
+ agNodeA: IRowNode;
172
+ agNodeB: IRowNode;
172
173
  column: Column;
173
174
  gridModel: GridModel;
174
175
  defaultComparator: (a: T, b: T) => number;
@@ -14,7 +14,7 @@ import {
14
14
  RecordActionSpec,
15
15
  StoreRecord
16
16
  } from '@xh/hoist/data';
17
- import {apiRemoved, throwIf, warnIf, withDefault} from '@xh/hoist/utils/js';
17
+ import {apiRemoved, logDebug, logWarn, throwIf, warnIf, withDefault} from '@xh/hoist/utils/js';
18
18
  import classNames from 'classnames';
19
19
  import {
20
20
  castArray,
@@ -932,12 +932,12 @@ export class Column {
932
932
  sortCfg = find(gridModel.sortBy, {colId}),
933
933
  sortDir = sortCfg?.sort || 'asc',
934
934
  abs = sortCfg?.abs || false,
935
- recordA = agNodeA?.data,
936
- recordB = agNodeB?.data,
935
+ recordA = agNodeA?.data as StoreRecord,
936
+ recordB = agNodeB?.data as StoreRecord,
937
937
  params = {
938
938
  recordA,
939
939
  recordB,
940
- column: this,
940
+ column: this as Column,
941
941
  gridModel,
942
942
  defaultComparator: this.defaultComparator,
943
943
  agNodeA,
@@ -962,7 +962,7 @@ export class Column {
962
962
  if (editor) {
963
963
  ret.cellEditor = forwardRef((agParams: PlainObject, ref) => {
964
964
  const props = {
965
- record: agParams.data,
965
+ record: agParams.data as StoreRecord,
966
966
  gridModel,
967
967
  column: this,
968
968
  agParams,
@@ -1056,23 +1056,23 @@ export class Column {
1056
1056
 
1057
1057
  private parseFilterable(filterable) {
1058
1058
  if (!filterable) return false;
1059
+ const {colId} = this;
1059
1060
 
1060
1061
  if (XH.isMobileApp) {
1061
- console.warn(`'filterable' is not supported on mobile and will be ignored.`);
1062
+ logDebug(
1063
+ `Column ${colId} specs 'filterable' but not supported on mobile - will be ignored.`,
1064
+ this
1065
+ );
1062
1066
  return false;
1063
1067
  }
1064
1068
 
1065
1069
  if (!this.field) {
1066
- console.warn(
1067
- `Column '${this.colId}' is not a Store field. 'filterable' will be ignored.`
1068
- );
1070
+ logWarn(`Column ${colId} is not a Store field. 'filterable' will be ignored.`, this);
1069
1071
  return false;
1070
1072
  }
1071
1073
 
1072
1074
  if (this.field === 'cubeLabel') {
1073
- console.warn(
1074
- `Column '${this.colId}' is a cube label column. 'filterable' will be ignored.`
1075
- );
1075
+ logWarn(`Column ${colId} is a cube label column. 'filterable' will be ignored.`, this);
1076
1076
  return false;
1077
1077
  }
1078
1078
 
@@ -4,17 +4,17 @@
4
4
  *
5
5
  * Copyright © 2023 Extremely Heavy Industries Inc.
6
6
  */
7
- import {HAlign, PlainObject, Some, Thunkable} from '@xh/hoist/core';
7
+ import {HAlign, PlainObject, Some, Thunkable, XH} from '@xh/hoist/core';
8
8
  import {genDisplayName} from '@xh/hoist/data';
9
+
10
+ import type {ColGroupDef} from '@xh/hoist/kit/ag-grid';
9
11
  import {throwIf, withDefault} from '@xh/hoist/utils/js';
10
- import {clone, isEmpty, isFunction, isString} from 'lodash';
12
+ import {clone, isEmpty, isFunction, isString, keysIn} from 'lodash';
11
13
  import {ReactNode} from 'react';
12
14
  import {GridModel} from '../GridModel';
13
15
  import {ColumnHeaderClassFn, ColumnHeaderNameFn} from '../Types';
14
16
  import {Column, ColumnSpec, getAgHeaderClassFn} from './Column';
15
17
 
16
- import type {ColGroupDef} from '@xh/hoist/kit/ag-grid';
17
-
18
18
  export interface ColumnGroupSpec {
19
19
  /** Column or ColumnGroup configs for children of this group.*/
20
20
  children: Array<ColumnGroupSpec | ColumnSpec>;
@@ -39,6 +39,8 @@ export interface ColumnGroupSpec {
39
39
 
40
40
  /** True to skip this column when adding to grid. */
41
41
  omit?: Thunkable<boolean>;
42
+
43
+ appData?: PlainObject;
42
44
  }
43
45
 
44
46
  /**
@@ -62,6 +64,8 @@ export class ColumnGroup {
62
64
  */
63
65
  agOptions?: PlainObject;
64
66
 
67
+ appData: PlainObject;
68
+
65
69
  /**
66
70
  * Not for application use. ColumnGroups are created internally by Hoist.
67
71
  *
@@ -83,6 +87,7 @@ export class ColumnGroup {
83
87
  headerAlign,
84
88
  agOptions,
85
89
  borders,
90
+ appData,
86
91
  ...rest
87
92
  } = config;
88
93
 
@@ -92,8 +97,6 @@ export class ColumnGroup {
92
97
  'Must specify groupId or a string headerName for a ColumnGroup'
93
98
  );
94
99
 
95
- Object.assign(this, rest);
96
-
97
100
  this.groupId = withDefault(groupId, headerName) as string;
98
101
  this.headerName = withDefault(headerName, genDisplayName(this.groupId));
99
102
  this.headerClass = headerClass;
@@ -102,6 +105,14 @@ export class ColumnGroup {
102
105
  this.children = children;
103
106
  this.gridModel = gridModel;
104
107
  this.agOptions = agOptions ? clone(agOptions) : {};
108
+ this.appData = appData ? clone(appData) : {};
109
+
110
+ if (!isEmpty(rest)) {
111
+ const keys = keysIn(rest);
112
+ throw XH.exception(
113
+ `Column group '${this.groupId}' configured with unsupported key(s) '${keys}'. Custom config data must be nested within the 'appData' property.`
114
+ );
115
+ }
105
116
  }
106
117
 
107
118
  getAgSpec(): ColGroupDef {