@xh/hoist 78.0.0 → 78.1.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## 78.1.1 - 2025-12-03
4
+
5
+ ### 🐞 Bug Fixes
6
+ * Fix to Highchart timezone handling regression from version 77. Applications should note that
7
+ Highcharts has deprecated the `time.useUTC` option and its functioning seem suspect. Apps
8
+ should set `time.timezone` instead. See https://api.highcharts.com/highcharts/time.useUTC.
9
+
10
+ ### ⚙️ Technical
11
+
12
+ * Allow cross-tab persistence of client log levels
13
+
14
+ ## 78.1.0 - 2025-12-02
15
+
16
+ ### ⚙️ Technical
17
+ * New property `MsalClientConfig.enableSsoSilent` to govern use of MSAL SSO api.
18
+
19
+ * Existing property `MsalClientConfig.enableTelemetry` now defaults to `true`.
20
+
21
+ * Improved use of MSAL client API, to maximize effectiveness of SSO. Improved documentation
22
+ and logging. Iframe attempts will now time out by default after 3 seconds vs. 10 seconds.
23
+ This can be further modified by apps via the option
24
+ `MsalClientConfig.msalClientOptions.system.iFrameHashTimeout`
25
+
26
+ ### 📚 Libraries
27
+
28
+ * @auth0/auth0-spa-js `2.7 → 2.9`
29
+ * @azure/msal-browser `4.25 → 4.26`
30
+
3
31
  ## 78.0.0 - 2025-11-21
4
32
 
5
33
  ### 💥 Breaking Changes
@@ -33,6 +61,12 @@
33
61
 
34
62
  ## 77.1.1 - 2025-11-12
35
63
 
64
+ ### 💥 Breaking Changes (upgrade difficulty: 🟢 LOW)
65
+
66
+ * Apps that use and provide the `highcharts` library should be sure to update the version to v12.4.0.
67
+ Refer to `Bootstrap.js` in Toolbox for required import changes.
68
+ * Visit https://www.highcharts.com/blog/changelog/ for specific changes.
69
+
36
70
  ### 🎁 New Features
37
71
 
38
72
  * New method `StoreRecord.getModifiedValues()` to gather edited data from a store record.
@@ -64,7 +64,7 @@ const tbar = hoistCmp.factory<DifferModel>(({model}) => {
64
64
  placeholder: 'https://remote-host/',
65
65
  enableCreate: true,
66
66
  createMessageFn: identity,
67
- width: 250,
67
+ width: 350,
68
68
  options: model.remoteHosts
69
69
  }),
70
70
  button({
@@ -60,8 +60,14 @@ export class DifferModel extends HoistModel {
60
60
  recordsRequired: true
61
61
  };
62
62
 
63
+ /**
64
+ * All other configured appInstances URLs, excepting the current one.
65
+ * (Use of startsWith allows configs to end in trailing /)
66
+ */
63
67
  get remoteHosts(): string[] {
64
- return XH.getConf('xhAppInstances').filter(it => it !== window.location.origin);
68
+ return XH.getConf('xhAppInstances').filter(
69
+ (it: string) => !it.startsWith(window.location.origin)
70
+ );
65
71
  }
66
72
 
67
73
  constructor({
@@ -83,6 +89,9 @@ export class DifferModel extends HoistModel {
83
89
 
84
90
  this.url = entityName + 'DiffAdmin';
85
91
 
92
+ // Default to first available remote for comparison
93
+ this.remoteHost = this.remoteHosts[0];
94
+
86
95
  const rendererIsComplex = true;
87
96
  this.gridModel = new GridModel({
88
97
  store: {
@@ -24,6 +24,10 @@ export declare class DifferModel extends HoistModel {
24
24
  hasLoaded: boolean;
25
25
  get readonly(): boolean;
26
26
  applyRemoteAction: RecordActionSpec;
27
+ /**
28
+ * All other configured appInstances URLs, excepting the current one.
29
+ * (Use of startsWith allows configs to end in trailing /)
30
+ */
27
31
  get remoteHosts(): string[];
28
32
  constructor({ parentModel, entityName, displayName, columnFields, matchFields, valueRenderer }: Partial<DifferModel>);
29
33
  doLoadAsync(loadSpec: LoadSpec): Promise<void>;
@@ -326,39 +326,39 @@ export declare class GridModel extends HoistModel {
326
326
  localExport(filename: string, type: 'excel' | 'csv', params?: PlainObject): void;
327
327
  /**
328
328
  * Select records in the grid.
329
- *
330
329
  * @param records - one or more record(s) / ID(s) to select.
331
- * @param options - additional options containing the following keys:
332
- * ensureVisible - true to make selection visible if it is within a
333
- * collapsed node or outside of the visible scroll window. Default true.
334
- * clearSelection - true to clear previous selection (rather than
335
- * add to it). Default true.
330
+ * @param opts - additional post-selection options
336
331
  */
337
332
  selectAsync(records: Some<StoreRecordOrId>, opts?: {
333
+ /**
334
+ * True (default) to scroll the grid or expand nodes as needed to make selection
335
+ * visible if it is within a collapsed node or outside of the visible scroll window.
336
+ */
338
337
  ensureVisible?: boolean;
338
+ /** True (default) to clear previous selection (rather than add to it). */
339
339
  clearSelection?: boolean;
340
340
  }): Promise<void>;
341
341
  /**
342
342
  * Select the first row in the grid.
343
343
  *
344
- * See {@link preSelectFirstAsync} for a useful variant of this method. preSelectFirstAsync()
345
- * will not change the selection if there is already a selection, which is what applications
346
- * typically want to do when loading/reloading a grid.
347
- *
348
- * @param opts -
349
- * expandParentGroups - set to true to expand nodes to allow selection when the
350
- * first selectable node is in a collapsed group. Default true.
351
- * ensureVisible - set to to true to scroll to the selected row if it is outside of the
352
- * visible scroll window. Default true.
353
- *
344
+ * See {@link preSelectFirstAsync} for a useful variant of this method that will leave the
345
+ * any pre-existing selection unchanged, which is what apps typically want when reloading an
346
+ * already-populated grid.
354
347
  */
355
348
  selectFirstAsync(opts?: {
349
+ /**
350
+ * True (default) to expand nodes as needed to allow selection when the first selectable
351
+ * node is in a collapsed group.
352
+ */
356
353
  expandParentGroups?: boolean;
354
+ /**
355
+ * True (default) to scroll the grid or expand nodes as needed to make selection
356
+ * visible if it is outside of the visible scroll window.
357
+ */
357
358
  ensureVisible?: boolean;
358
359
  }): Promise<void>;
359
360
  /**
360
361
  * Select the first row in the grid, if no other selection present.
361
- *
362
362
  * This method delegates to {@link selectFirstAsync}.
363
363
  */
364
364
  preSelectFirstAsync(): Promise<void>;
@@ -186,11 +186,11 @@ export declare class XHApi {
186
186
  get logLevel(): LogLevel;
187
187
  /**
188
188
  * Set the minimum severity for Hoist log utils until the page is refreshed. Optionally persist
189
- * this adjustment to sessionStorage to maintain for the lifetime of the browser tab.
189
+ * this adjustment for up to 1440 minutes in local storage.
190
190
  *
191
191
  * Hint: call this method from the console to adjust your app's log level while troubleshooting.
192
192
  */
193
- setLogLevel(level: LogLevel, persistInSessionStorage?: boolean): void;
193
+ setLogLevel(level: LogLevel, persistMins?: number): void;
194
194
  /**
195
195
  * Main entry point to start the client app - initializes and renders application code.
196
196
  * Call from the app's entry-point file within your project's `/client-app/src/apps/` folder.
@@ -19,7 +19,7 @@ export interface MsalClientConfig extends BaseOAuthClientConfig<MsalTokenSpec> {
19
19
  domainHint?: string;
20
20
  /**
21
21
  * True to enable support for built-in telemetry provided by this class's internal MSAL client.
22
- * Captured performance events will be summarized as {@link MsalClientTelemetry}.
22
+ * Captured performance events will be summarized as {@link MsalClientTelemetry}. Default true.
23
23
  */
24
24
  enableTelemetry?: boolean;
25
25
  /**
@@ -42,6 +42,17 @@ export interface MsalClientConfig extends BaseOAuthClientConfig<MsalTokenSpec> {
42
42
  * See https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/token-lifetimes.md
43
43
  */
44
44
  initRefreshTokenExpirationOffsetSecs?: number;
45
+ /**
46
+ * Enable the use of the MSAL ssoSilent() API, which will attempt to use credentials gained by
47
+ * another app or tab to start a new session for this app. Requires iFrames and 3rd party
48
+ * cookies to be enabled. Default true.
49
+ *
50
+ * In practice, and according to documentation, this operation is likely to fail for a
51
+ * number of reasons, and can often do so as timeout. Therefore, keeping the timeout limit
52
+ * value -- `system.iFrameHashTimeout` -- at a relatively low value is critical. Hoist
53
+ * defaults this value to 3000ms vs. the default 10000ms.
54
+ */
55
+ enableSsoSilent?: boolean;
45
56
  /** The log level of MSAL. Default is LogLevel.Warning. */
46
57
  msalLogLevel?: LogLevel;
47
58
  /**
@@ -105,14 +116,19 @@ export declare class MsalClient extends BaseOAuthClient<MsalClientConfig, MsalTo
105
116
  private get loginScopes();
106
117
  private get loginExtraScopesToConsent();
107
118
  private get refreshOffsetArgs();
108
- private noteUserAuthenticated;
119
+ private setAccount;
120
+ private noteAuthComplete;
121
+ private authRequestCore;
109
122
  }
123
+ type AuthMethod = 'acquireSilent' | 'ssoSilent' | 'loginPopup' | 'loginRedirect';
110
124
  /**
111
125
  * Telemetry produced by this client (if enabled) + included in {@link ClientHealthService}
112
126
  * reporting. Leverages MSAL's opt-in support for emitting performance events.
113
127
  * See https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/performance.md
114
128
  */
115
129
  interface MsalClientTelemetry {
130
+ /** Method of last authentication for this client. */
131
+ authMethod: AuthMethod;
116
132
  /** Stats across all events */
117
133
  summary: {
118
134
  successCount: number;
@@ -44,11 +44,11 @@ export interface APIWarnOptions {
44
44
  export declare function getLogLevel(): LogLevel;
45
45
  /**
46
46
  * Set the minimum severity for Hoist log utils until the page is refreshed. Optionally persist
47
- * this adjustment to sessionStorage to maintain for the lifetime of the browser tab.
47
+ * this adjustment to localStorage for up to 24 hours.
48
48
  *
49
49
  * @internal - use public `XH.setLogLevel()`.
50
50
  */
51
- export declare function setLogLevel(level: LogLevel, persistInSessionStorage?: boolean): void;
51
+ export declare function setLogLevel(level: LogLevel, persistMins?: number): void;
52
52
  /**
53
53
  * Time and log execution of a function to `console.info()`.
54
54
  *
@@ -751,19 +751,22 @@ export class GridModel extends HoistModel {
751
751
 
752
752
  /**
753
753
  * Select records in the grid.
754
- *
755
754
  * @param records - one or more record(s) / ID(s) to select.
756
- * @param options - additional options containing the following keys:
757
- * ensureVisible - true to make selection visible if it is within a
758
- * collapsed node or outside of the visible scroll window. Default true.
759
- * clearSelection - true to clear previous selection (rather than
760
- * add to it). Default true.
755
+ * @param opts - additional post-selection options
761
756
  */
762
757
  async selectAsync(
763
758
  records: Some<StoreRecordOrId>,
764
- opts?: {ensureVisible?: boolean; clearSelection?: boolean}
759
+ opts: {
760
+ /**
761
+ * True (default) to scroll the grid or expand nodes as needed to make selection
762
+ * visible if it is within a collapsed node or outside of the visible scroll window.
763
+ */
764
+ ensureVisible?: boolean;
765
+ /** True (default) to clear previous selection (rather than add to it). */
766
+ clearSelection?: boolean;
767
+ } = {}
765
768
  ) {
766
- const {ensureVisible = true, clearSelection = true} = opts ?? {};
769
+ const {ensureVisible = true, clearSelection = true} = opts;
767
770
  this.selModel.select(records, clearSelection);
768
771
  if (ensureVisible) await this.ensureSelectionVisibleAsync();
769
772
  }
@@ -771,19 +774,25 @@ export class GridModel extends HoistModel {
771
774
  /**
772
775
  * Select the first row in the grid.
773
776
  *
774
- * See {@link preSelectFirstAsync} for a useful variant of this method. preSelectFirstAsync()
775
- * will not change the selection if there is already a selection, which is what applications
776
- * typically want to do when loading/reloading a grid.
777
- *
778
- * @param opts -
779
- * expandParentGroups - set to true to expand nodes to allow selection when the
780
- * first selectable node is in a collapsed group. Default true.
781
- * ensureVisible - set to to true to scroll to the selected row if it is outside of the
782
- * visible scroll window. Default true.
783
- *
777
+ * See {@link preSelectFirstAsync} for a useful variant of this method that will leave the
778
+ * any pre-existing selection unchanged, which is what apps typically want when reloading an
779
+ * already-populated grid.
784
780
  */
785
- async selectFirstAsync(opts?: {expandParentGroups?: boolean; ensureVisible?: boolean}) {
786
- const {expandParentGroups = true, ensureVisible = true} = opts ?? {};
781
+ async selectFirstAsync(
782
+ opts: {
783
+ /**
784
+ * True (default) to expand nodes as needed to allow selection when the first selectable
785
+ * node is in a collapsed group.
786
+ */
787
+ expandParentGroups?: boolean;
788
+ /**
789
+ * True (default) to scroll the grid or expand nodes as needed to make selection
790
+ * visible if it is outside of the visible scroll window.
791
+ */
792
+ ensureVisible?: boolean;
793
+ } = {}
794
+ ) {
795
+ const {expandParentGroups = true, ensureVisible = true} = opts;
787
796
  await this.whenReadyAsync();
788
797
  if (!this.isReady) return;
789
798
 
@@ -803,7 +812,6 @@ export class GridModel extends HoistModel {
803
812
 
804
813
  /**
805
814
  * Select the first row in the grid, if no other selection present.
806
- *
807
815
  * This method delegates to {@link selectFirstAsync}.
808
816
  */
809
817
  async preSelectFirstAsync() {
package/core/XH.ts CHANGED
@@ -371,12 +371,12 @@ export class XHApi {
371
371
 
372
372
  /**
373
373
  * Set the minimum severity for Hoist log utils until the page is refreshed. Optionally persist
374
- * this adjustment to sessionStorage to maintain for the lifetime of the browser tab.
374
+ * this adjustment for up to 1440 minutes in local storage.
375
375
  *
376
376
  * Hint: call this method from the console to adjust your app's log level while troubleshooting.
377
377
  */
378
- setLogLevel(level: LogLevel, persistInSessionStorage: boolean = false) {
379
- setLogLevel(level, persistInSessionStorage);
378
+ setLogLevel(level: LogLevel, persistMins: number = -1) {
379
+ setLogLevel(level, persistMins);
380
380
  }
381
381
 
382
382
  //----------------------
package/data/Store.ts CHANGED
@@ -763,7 +763,10 @@ export class Store extends HoistBase {
763
763
  /** True if the store has changes which need to be committed. */
764
764
  @computed
765
765
  get isDirty(): boolean {
766
- return this._current !== this._committed || this.summaryRecords?.some(it => it.isModified);
766
+ return (
767
+ this._current !== this._committed ||
768
+ (this.summaryRecords?.some(it => it.isModified) ?? false)
769
+ );
767
770
  }
768
771
 
769
772
  /** Alias for {@link Store.isDirty} */
@@ -27,8 +27,8 @@ export function installHighcharts(HighchartsImpl) {
27
27
  }
28
28
 
29
29
  HighchartsImpl.setOptions({
30
- global: {
31
- useUTC: false
30
+ time: {
31
+ timezone: undefined
32
32
  },
33
33
  lang: {
34
34
  thousandsSep: ','
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xh/hoist",
3
- "version": "78.0.0",
3
+ "version": "78.1.1",
4
4
  "description": "Hoist add-on for building and deploying React Applications.",
5
5
  "repository": "github:xh/hoist-react",
6
6
  "homepage": "https://xh.io",
@@ -28,8 +28,8 @@
28
28
  ]
29
29
  },
30
30
  "dependencies": {
31
- "@auth0/auth0-spa-js": "~2.7.0",
32
- "@azure/msal-browser": "~4.25.0",
31
+ "@auth0/auth0-spa-js": "~2.9.1",
32
+ "@azure/msal-browser": "~4.26.2",
33
33
  "@blueprintjs/core": "^5.10.5",
34
34
  "@blueprintjs/datetime": "^5.3.7",
35
35
  "@blueprintjs/datetime2": "^2.3.7",
@@ -180,8 +180,8 @@ export abstract class BaseOAuthClient<
180
180
  * Request a full logout from the underlying OAuth provider.
181
181
  */
182
182
  async logoutAsync(): Promise<void> {
183
- await this.doLogoutAsync();
184
183
  this.setSelectedUsername(null);
184
+ await this.doLogoutAsync();
185
185
  }
186
186
 
187
187
  /**