@xh/hoist 66.0.0 β 66.0.2
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 +43 -13
- package/admin/tabs/userData/roles/RoleModel.ts +34 -40
- package/build/types/admin/tabs/userData/roles/RoleModel.d.ts +4 -5
- package/build/types/desktop/cmp/contextmenu/ContextMenu.d.ts +5 -6
- package/build/types/desktop/cmp/panel/Panel.d.ts +1 -1
- package/build/types/desktop/hooks/UseContextMenu.d.ts +5 -5
- package/build/types/security/BaseOAuthClient.d.ts +5 -5
- package/build/types/security/msal/MsalClient.d.ts +12 -10
- package/desktop/cmp/contextmenu/ContextMenu.ts +6 -6
- package/desktop/cmp/grid/editors/NumberEditor.ts +12 -0
- package/desktop/cmp/input/NumberInput.ts +7 -4
- package/desktop/cmp/panel/Panel.ts +1 -1
- package/desktop/hooks/UseContextMenu.ts +12 -12
- package/mobile/cmp/input/NumberInput.ts +7 -4
- package/package.json +1 -1
- package/security/BaseOAuthClient.ts +11 -11
- package/security/msal/MsalClient.ts +19 -16
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,19 +1,38 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 66.0.2 - 2024-07-17
|
|
4
|
+
|
|
5
|
+
### π Bug Fixes
|
|
6
|
+
|
|
7
|
+
* Improved redirect handling within beta `MsalClient` to use Hoist-provided blank URL (an empty,
|
|
8
|
+
static page) for all iFrame-based "silent" token requests, as per MS recommendations. Intended to
|
|
9
|
+
avoid potential race conditions triggered by redirecting to the base app URL in these cases.
|
|
10
|
+
* Fixed bug where `ContextMenu` items could be improperly positioned.
|
|
11
|
+
* β οΈNote that `MenuItems` inside a desktop `ContextMenu` are now rendered in a portal, outside
|
|
12
|
+
the normal component hierarchy, to ensures that menu items are positioned properly relative to
|
|
13
|
+
their parent. It should not affect most apps, but could impact menu style customizations that
|
|
14
|
+
rely on specific CSS selectors targeting the previous DOM structure.
|
|
15
|
+
|
|
16
|
+
## 66.0.1 - 2024-07-10
|
|
17
|
+
|
|
18
|
+
### π Bug Fixes
|
|
19
|
+
|
|
20
|
+
* Fixed bug where inline grid edit of `NumberInput` was lost after quick navigation.
|
|
21
|
+
|
|
3
22
|
## 66.0.0 - 2024-07-09
|
|
4
23
|
|
|
5
|
-
### π₯ Breaking Changes (upgrade difficulty:
|
|
24
|
+
### π₯ Breaking Changes (upgrade difficulty: π’ LOW - minor adjustments to client-side auth)
|
|
6
25
|
|
|
7
26
|
* New `HoistAuthModel` exposes the client-side authentication lifecycle via a newly consolidated,
|
|
8
27
|
overridable API. This new API provides more easy customization of auth across all client-side
|
|
9
28
|
apps by being easily overrideable and specified via the `AppSpec` passed to `XH.renderApp()`.
|
|
10
|
-
* In most cases, upgrading should be a simple matter of moving code
|
|
11
|
-
|
|
12
|
-
|
|
29
|
+
* In most cases, upgrading should be a simple matter of moving code from `HoistAppModel` methods
|
|
30
|
+
`preAuthInitAsync()` and `logoutAsync()` (removed by this change) to new `HoistAuthModel`
|
|
31
|
+
methods `completeAuthAsync()` and `logoutAsync()`.
|
|
13
32
|
|
|
14
33
|
### π New Features
|
|
15
34
|
|
|
16
|
-
*
|
|
35
|
+
* Added option to `XH.reloadApp()` to reload specific app path.
|
|
17
36
|
* Added `headerTooltip` prop to `ColumnGroup`.
|
|
18
37
|
|
|
19
38
|
### π Bug Fixes
|
|
@@ -23,7 +42,7 @@
|
|
|
23
42
|
an unexpected gap across the bottom of the screen. Includes fallback for secure client browsers
|
|
24
43
|
that don't support dynamic viewport units.
|
|
25
44
|
* Updated mobile `TabContainer` to flex properly within flexbox containers.
|
|
26
|
-
* Fixed timing issue with missing validation for records added immediately to new
|
|
45
|
+
* Fixed timing issue with missing validation for records added immediately to a new `Store`.
|
|
27
46
|
* Fixed CSS bug in which date picker dates wrapped when `dateEditor` used in a grid in a dialog.
|
|
28
47
|
|
|
29
48
|
## 65.0.0 - 2024-06-26
|
|
@@ -269,13 +288,24 @@ details.
|
|
|
269
288
|
There are some common breaking changes that most/many apps will need to address:
|
|
270
289
|
|
|
271
290
|
* CSS rules with the `bp4-` prefix should be updated to use the `bp5-` prefix.
|
|
272
|
-
*
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
*
|
|
277
|
-
|
|
278
|
-
|
|
291
|
+
* Popovers
|
|
292
|
+
* For `popover` and `tooltip` components, replace `target` with `item` if using elementFactory.
|
|
293
|
+
If using JSX, replace `target` prop with a child element. Also applies to the
|
|
294
|
+
mobile `popover`.
|
|
295
|
+
* Popovers no longer have a popover-wrapper element - remove/replace any CSS rules
|
|
296
|
+
targeting `bp4-popover-wrapper`.
|
|
297
|
+
* All components which render popovers now depend
|
|
298
|
+
on [`popper.js v2.x`](https://popper.js.org/docs/v2/). Complex customizations to popovers may
|
|
299
|
+
need to be reworked.
|
|
300
|
+
* A breaking change to `Popover` in BP5 was splitting the `boundary` prop into `rootBoundary`
|
|
301
|
+
and `boundary`:
|
|
302
|
+
Popovers were frequently set up with `boundary: 'viewport'`, which is no longer valid since
|
|
303
|
+
"viewport" can be assigned to the `rootBoundary` but not to the `boundary`.
|
|
304
|
+
However, viewport is the DEFAULT value for `rootBoundary`
|
|
305
|
+
per [popper.js docs](https://popper.js.org/docs/v2/utils/detect-overflow/#boundary),
|
|
306
|
+
so `boundary: 'viewport'` should be safe to remove entirely.
|
|
307
|
+
* [see Blueprint's Popover2 migration guide](https://github.com/palantir/blueprint/wiki/Popover2-migration)
|
|
308
|
+
* [see Popover2's `boundary` & `rootBoundary` docs](https://popper.js.org/docs/v2/utils/detect-overflow/#boundary)
|
|
279
309
|
* Where applicable, the former `elementRef` prop has been replaced by the simpler, more
|
|
280
310
|
straightforward `ref` prop using `React.forwardRef()` - e.g. Hoist's `button.elementRef` prop
|
|
281
311
|
becomes just `ref`. Review your app for uses of `elementRef`.
|
|
@@ -17,7 +17,7 @@ import {wait} from '@xh/hoist/promise';
|
|
|
17
17
|
import {compact, groupBy, mapValues} from 'lodash';
|
|
18
18
|
import moment from 'moment/moment';
|
|
19
19
|
import {RoleEditorModel} from './editor/RoleEditorModel';
|
|
20
|
-
import {HoistRole,
|
|
20
|
+
import {HoistRole, RoleModuleConfig} from './Types';
|
|
21
21
|
|
|
22
22
|
export class RoleModel extends HoistModel {
|
|
23
23
|
static PERSIST_WITH = {localStorageKey: 'xhAdminRolesState'};
|
|
@@ -100,7 +100,26 @@ export class RoleModel extends HoistModel {
|
|
|
100
100
|
this.gridModel.clear();
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
async createAsync(roleSpec?: HoistRole): Promise<void> {
|
|
104
|
+
if (this.readonly) return;
|
|
105
|
+
|
|
106
|
+
const addedRole = await this.roleEditorModel.createAsync(roleSpec);
|
|
107
|
+
if (!addedRole) return;
|
|
108
|
+
await this.refreshAsync();
|
|
109
|
+
await this.gridModel.selectAsync(addedRole.name);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async editAsync(role: HoistRole): Promise<void> {
|
|
113
|
+
if (this.readonly) return;
|
|
114
|
+
|
|
115
|
+
const updatedRole = await this.roleEditorModel.editAsync(role);
|
|
116
|
+
if (!updatedRole) return;
|
|
117
|
+
await this.refreshAsync();
|
|
118
|
+
}
|
|
119
|
+
|
|
103
120
|
async deleteAsync(role: HoistRole): Promise<boolean> {
|
|
121
|
+
if (this.readonly) return false;
|
|
122
|
+
|
|
104
123
|
const confirm = await XH.confirm({
|
|
105
124
|
icon: Icon.warning(),
|
|
106
125
|
title: 'Confirm delete?',
|
|
@@ -108,17 +127,18 @@ export class RoleModel extends HoistModel {
|
|
|
108
127
|
confirmProps: {intent: 'danger', text: 'Confirm Delete'}
|
|
109
128
|
});
|
|
110
129
|
if (!confirm) return false;
|
|
130
|
+
|
|
111
131
|
await XH.fetchJson({
|
|
112
132
|
url: `roleAdmin/delete/${role.name}`,
|
|
113
133
|
method: 'DELETE'
|
|
114
134
|
});
|
|
115
|
-
this.refreshAsync();
|
|
135
|
+
await this.refreshAsync();
|
|
116
136
|
return true;
|
|
117
137
|
}
|
|
118
138
|
|
|
119
|
-
|
|
139
|
+
//------------------
|
|
120
140
|
// Actions
|
|
121
|
-
|
|
141
|
+
//------------------
|
|
122
142
|
addAction(): RecordActionSpec {
|
|
123
143
|
return {
|
|
124
144
|
text: 'Add',
|
|
@@ -142,7 +162,7 @@ export class RoleModel extends HoistModel {
|
|
|
142
162
|
};
|
|
143
163
|
}
|
|
144
164
|
|
|
145
|
-
cloneAction(): RecordActionSpec {
|
|
165
|
+
private cloneAction(): RecordActionSpec {
|
|
146
166
|
return {
|
|
147
167
|
text: 'Clone',
|
|
148
168
|
icon: Icon.copy(),
|
|
@@ -154,7 +174,7 @@ export class RoleModel extends HoistModel {
|
|
|
154
174
|
};
|
|
155
175
|
}
|
|
156
176
|
|
|
157
|
-
deleteAction(): RecordActionSpec {
|
|
177
|
+
private deleteAction(): RecordActionSpec {
|
|
158
178
|
return {
|
|
159
179
|
text: 'Delete',
|
|
160
180
|
icon: Icon.delete(),
|
|
@@ -181,16 +201,10 @@ export class RoleModel extends HoistModel {
|
|
|
181
201
|
}
|
|
182
202
|
};
|
|
183
203
|
}
|
|
184
|
-
async editAsync(role: HoistRole): Promise<void> {
|
|
185
|
-
const updatedRole = await this.roleEditorModel.editAsync(role);
|
|
186
|
-
if (!updatedRole) return;
|
|
187
|
-
await this.refreshAsync();
|
|
188
|
-
}
|
|
189
204
|
|
|
190
|
-
|
|
205
|
+
//------------------
|
|
191
206
|
// Implementation
|
|
192
|
-
|
|
193
|
-
|
|
207
|
+
//------------------
|
|
194
208
|
private displayRoles() {
|
|
195
209
|
const {gridModel} = this,
|
|
196
210
|
gridData = this.showInGroups
|
|
@@ -250,13 +264,6 @@ export class RoleModel extends HoistModel {
|
|
|
250
264
|
return root;
|
|
251
265
|
}
|
|
252
266
|
|
|
253
|
-
private async createAsync(roleSpec?: HoistRole): Promise<void> {
|
|
254
|
-
const addedRole = await this.roleEditorModel.createAsync(roleSpec);
|
|
255
|
-
if (!addedRole) return;
|
|
256
|
-
await this.refreshAsync();
|
|
257
|
-
await this.gridModel.selectAsync(addedRole.name);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
267
|
private createGridModel(): GridModel {
|
|
261
268
|
return new GridModel({
|
|
262
269
|
treeMode: true,
|
|
@@ -272,13 +279,6 @@ export class RoleModel extends HoistModel {
|
|
|
272
279
|
'xh-grid-clear-background-color': ({data}) => !data.data.isGroupRow
|
|
273
280
|
},
|
|
274
281
|
headerMenuDisplay: 'hover',
|
|
275
|
-
onRowDoubleClicked: ({data: record}) => {
|
|
276
|
-
if (!this.readonly && record && record.data.isGroupRow) {
|
|
277
|
-
this.roleEditorModel
|
|
278
|
-
.editAsync(record.data)
|
|
279
|
-
.then(role => role && this.refreshAsync());
|
|
280
|
-
}
|
|
281
|
-
},
|
|
282
282
|
persistWith: {...this.persistWith, path: 'mainGrid'},
|
|
283
283
|
store: {
|
|
284
284
|
idSpec: ({id, name}) => {
|
|
@@ -344,7 +344,12 @@ export class RoleModel extends HoistModel {
|
|
|
344
344
|
'-',
|
|
345
345
|
this.groupByAction(),
|
|
346
346
|
...GridModel.defaultContextMenu
|
|
347
|
-
]
|
|
347
|
+
],
|
|
348
|
+
onRowDoubleClicked: ({data: record}) => {
|
|
349
|
+
if (record && !record.data.isGroupRow) {
|
|
350
|
+
this.editAsync(record.data as HoistRole);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
348
353
|
});
|
|
349
354
|
}
|
|
350
355
|
|
|
@@ -384,15 +389,4 @@ export class RoleModel extends HoistModel {
|
|
|
384
389
|
persistWith: {...RoleModel.PERSIST_WITH, path: 'mainFilterChooser'}
|
|
385
390
|
});
|
|
386
391
|
}
|
|
387
|
-
|
|
388
|
-
private getFieldForMemberType(type: RoleMemberType, effective: boolean): string {
|
|
389
|
-
switch (type) {
|
|
390
|
-
case 'USER':
|
|
391
|
-
return effective ? 'effectiveUserNames' : 'users';
|
|
392
|
-
case 'DIRECTORY_GROUP':
|
|
393
|
-
return effective ? 'effectiveDirectoryGroupNames' : 'directoryGroups';
|
|
394
|
-
case 'ROLE':
|
|
395
|
-
return effective ? 'effectiveRoleNames' : 'roles';
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
392
|
}
|
|
@@ -24,19 +24,18 @@ export declare class RoleModel extends HoistModel {
|
|
|
24
24
|
doLoadAsync(loadSpec: LoadSpec): Promise<void>;
|
|
25
25
|
selectRoleAsync(name: string): Promise<void>;
|
|
26
26
|
clear(): void;
|
|
27
|
+
createAsync(roleSpec?: HoistRole): Promise<void>;
|
|
28
|
+
editAsync(role: HoistRole): Promise<void>;
|
|
27
29
|
deleteAsync(role: HoistRole): Promise<boolean>;
|
|
28
30
|
addAction(): RecordActionSpec;
|
|
29
31
|
editAction(): RecordActionSpec;
|
|
30
|
-
cloneAction
|
|
31
|
-
deleteAction
|
|
32
|
+
private cloneAction;
|
|
33
|
+
private deleteAction;
|
|
32
34
|
private groupByAction;
|
|
33
|
-
editAsync(role: HoistRole): Promise<void>;
|
|
34
35
|
private displayRoles;
|
|
35
36
|
private ensureInitializedAsync;
|
|
36
37
|
private processRolesFromServer;
|
|
37
38
|
private processRolesForTreeGrid;
|
|
38
|
-
private createAsync;
|
|
39
39
|
private createGridModel;
|
|
40
40
|
private createFilterChooserModel;
|
|
41
|
-
private getFieldForMemberType;
|
|
42
41
|
}
|
|
@@ -2,7 +2,7 @@ import { HoistProps, MenuItemLike } from '@xh/hoist/core';
|
|
|
2
2
|
import '@xh/hoist/desktop/register';
|
|
3
3
|
import { ReactElement } from 'react';
|
|
4
4
|
/**
|
|
5
|
-
* A context menu is specified as
|
|
5
|
+
* A context menu is specified as an array of items, a function to generate one from a click, or
|
|
6
6
|
* a full element representing a contextMenu Component.
|
|
7
7
|
*/
|
|
8
8
|
export type ContextMenuSpec = MenuItemLike[] | ((e: MouseEvent) => MenuItemLike[]) | ReactElement;
|
|
@@ -10,12 +10,11 @@ export interface ContextMenuProps extends HoistProps {
|
|
|
10
10
|
menuItems: MenuItemLike[];
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
13
|
+
* Component for a right-click context menu. Not typically used directly by applications - use
|
|
14
|
+
* the {@link useContextMenu} hook to add a context menu to an app component, or leverage Panel's
|
|
15
|
+
* built-in support via {@link PanelProps.contextMenu}.
|
|
14
16
|
*
|
|
15
|
-
*
|
|
16
|
-
* see ContextMenuHost, or the `contextMenu` prop on panel.
|
|
17
|
-
*
|
|
18
|
-
* See {@link GridContextMenu} to specify a context menu on Grid and DataView components.
|
|
17
|
+
* See {@link GridContextMenuSpec} to specify a context menu on `Grid` and `DataView` components.
|
|
19
18
|
* That API will receive specific information about the current selection
|
|
20
19
|
*/
|
|
21
20
|
export declare const ContextMenu: import("react").FC<ContextMenuProps>, contextMenu: import("@xh/hoist/core").ElementFactory<ContextMenuProps>;
|
|
@@ -16,7 +16,7 @@ export interface PanelProps extends HoistProps<PanelModel>, Omit<BoxProps, 'titl
|
|
|
16
16
|
icon?: ReactElement;
|
|
17
17
|
/** Icon to be used when the panel is collapsed. Defaults to `icon`. */
|
|
18
18
|
collapsedIcon?: ReactElement;
|
|
19
|
-
/** Context
|
|
19
|
+
/** Context menu to show on a right-click within this panel. */
|
|
20
20
|
contextMenu?: ContextMenuSpec;
|
|
21
21
|
/**
|
|
22
22
|
* Specification of hotkeys as prescribed by blueprint.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { ContextMenuSpec } from '@xh/hoist/desktop/cmp/contextmenu/ContextMenu';
|
|
2
2
|
import { ReactElement } from 'react';
|
|
3
3
|
/**
|
|
4
|
-
* Hook to add context menu
|
|
4
|
+
* Hook to add a right-click context menu to a component.
|
|
5
5
|
*
|
|
6
|
-
* @param child - element to be given context menu support. Must specify
|
|
7
|
-
*
|
|
8
|
-
* @param spec -
|
|
9
|
-
*
|
|
6
|
+
* @param child - element to be given context menu support. Must specify a component that takes
|
|
7
|
+
* the React `onContextMenu` event as a prop (e.g. boxes, panel, div, etc.)
|
|
8
|
+
* @param spec - spec the menu to be shown. If null, or the number of items is empty, no menu will
|
|
9
|
+
* be rendered and the event will be consumed.
|
|
10
10
|
*/
|
|
11
11
|
export declare function useContextMenu(child?: ReactElement, spec?: ContextMenuSpec): ReactElement;
|
|
@@ -5,13 +5,13 @@ export interface BaseOAuthClientConfig<S> {
|
|
|
5
5
|
/** Client ID (GUID) of your app registered with your Oauth provider. */
|
|
6
6
|
clientId: string;
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* Redirect URL where authentication responses can be received by your application.
|
|
9
9
|
* It must exactly match one of the redirect URIs registered in the relevant OAuth authority.
|
|
10
10
|
* Default is 'APP_BASE_URL' which will be replaced with the current app's base URL.
|
|
11
11
|
*/
|
|
12
12
|
redirectUrl?: 'APP_BASE_URL' | string;
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
14
|
+
* Redirect URL after a successful logout.
|
|
15
15
|
* Default is 'APP_BASE_URL' which will be replaced with the current app's base URL.
|
|
16
16
|
*/
|
|
17
17
|
postLogoutRedirectUrl?: 'APP_BASE_URL' | string;
|
|
@@ -72,7 +72,7 @@ export interface BaseOAuthClientConfig<S> {
|
|
|
72
72
|
export declare abstract class BaseOAuthClient<C extends BaseOAuthClientConfig<S>, S> extends HoistBase {
|
|
73
73
|
/** Config loaded from UI server + init method. */
|
|
74
74
|
protected config: C;
|
|
75
|
-
/**
|
|
75
|
+
/** ID Scopes */
|
|
76
76
|
protected idScopes: string[];
|
|
77
77
|
/** Specification for Access Tokens **/
|
|
78
78
|
protected accessSpecs: Record<string, S>;
|
|
@@ -97,7 +97,7 @@ export declare abstract class BaseOAuthClient<C extends BaseOAuthClientConfig<S>
|
|
|
97
97
|
*/
|
|
98
98
|
getIdTokenAsync(): Promise<Token>;
|
|
99
99
|
/**
|
|
100
|
-
* Get
|
|
100
|
+
* Get an Access token.
|
|
101
101
|
*/
|
|
102
102
|
getAccessTokenAsync(key: string): Promise<Token>;
|
|
103
103
|
/**
|
|
@@ -107,7 +107,7 @@ export declare abstract class BaseOAuthClient<C extends BaseOAuthClientConfig<S>
|
|
|
107
107
|
/**
|
|
108
108
|
* The last authenticated OAuth username.
|
|
109
109
|
*
|
|
110
|
-
* Provided to facilitate more efficient re-login via SSO or otherwise.
|
|
110
|
+
* Provided to facilitate more efficient re-login via SSO or otherwise. Cleared on logout.
|
|
111
111
|
* Note: not necessarily a currently authenticated user, and not necessarily the Hoist username.
|
|
112
112
|
*/
|
|
113
113
|
getSelectedUsername(): string;
|
|
@@ -2,14 +2,13 @@ import { LogLevel } from '@azure/msal-browser';
|
|
|
2
2
|
import { Token, TokenMap } from '@xh/hoist/security/Token';
|
|
3
3
|
import { BaseOAuthClient, BaseOAuthClientConfig } from '../BaseOAuthClient';
|
|
4
4
|
export interface MsalClientConfig extends BaseOAuthClientConfig<MsalTokenSpec> {
|
|
5
|
-
/** Tenant ID (GUID) of your organization */
|
|
6
|
-
tenantId: string;
|
|
7
5
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* Authority for your organization's tenant: `https://login.microsoftonline.com/[tenantId]`.
|
|
7
|
+
* MSAL defaults to their "common" tenant (https://login.microsoftonline.com/common") to support
|
|
8
|
+
* auth with personal MS accounts, but enterprise/Hoist apps will almost certainly use a
|
|
9
|
+
* specific authority to point to their own private/corporate tenant.
|
|
11
10
|
*/
|
|
12
|
-
authority
|
|
11
|
+
authority: string;
|
|
13
12
|
/**
|
|
14
13
|
* A hint about the tenant or domain that the user should use to sign in.
|
|
15
14
|
* The value of the domain hint is a registered domain for the tenant.
|
|
@@ -63,10 +62,13 @@ export interface MsalTokenSpec {
|
|
|
63
62
|
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/token-lifetimes.md
|
|
64
63
|
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/login-user.md
|
|
65
64
|
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
65
|
+
* Also see this doc re. use of blankUrl as redirectUri for all "silent" token requests:
|
|
66
|
+
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/errors.md#issues-caused-by-the-redirecturi-page
|
|
67
|
+
*
|
|
68
|
+
* TODO: The handling of `ssoSilent` and `initRefreshTokenExpirationOffsetSecs` in this library
|
|
69
|
+
* require 3rd party cookies to be enabled in the browser so that MSAL can load contact in a
|
|
70
|
+
* hidden iFrame If its *not* enabled, we may be doing extra work. Consider checking 3rd party
|
|
71
|
+
* cookie support and adding conditional behavior?
|
|
70
72
|
*/
|
|
71
73
|
export declare class MsalClient extends BaseOAuthClient<MsalClientConfig, MsalTokenSpec> {
|
|
72
74
|
private client;
|
|
@@ -13,7 +13,7 @@ import {clone, isEmpty, isString} from 'lodash';
|
|
|
13
13
|
import {isValidElement, ReactElement, ReactNode} from 'react';
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
|
-
* A context menu is specified as
|
|
16
|
+
* A context menu is specified as an array of items, a function to generate one from a click, or
|
|
17
17
|
* a full element representing a contextMenu Component.
|
|
18
18
|
*/
|
|
19
19
|
export type ContextMenuSpec = MenuItemLike[] | ((e: MouseEvent) => MenuItemLike[]) | ReactElement;
|
|
@@ -23,12 +23,11 @@ export interface ContextMenuProps extends HoistProps {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
*
|
|
26
|
+
* Component for a right-click context menu. Not typically used directly by applications - use
|
|
27
|
+
* the {@link useContextMenu} hook to add a context menu to an app component, or leverage Panel's
|
|
28
|
+
* built-in support via {@link PanelProps.contextMenu}.
|
|
27
29
|
*
|
|
28
|
-
*
|
|
29
|
-
* see ContextMenuHost, or the `contextMenu` prop on panel.
|
|
30
|
-
*
|
|
31
|
-
* See {@link GridContextMenu} to specify a context menu on Grid and DataView components.
|
|
30
|
+
* See {@link GridContextMenuSpec} to specify a context menu on `Grid` and `DataView` components.
|
|
32
31
|
* That API will receive specific information about the current selection
|
|
33
32
|
*/
|
|
34
33
|
export const [ContextMenu, contextMenu] = hoistCmp.withFactory<ContextMenuProps>({
|
|
@@ -72,6 +71,7 @@ function parseItems(items: MenuItemLike[]): ReactNode[] {
|
|
|
72
71
|
intent: item.intent,
|
|
73
72
|
className: item.className,
|
|
74
73
|
onClick: item.actionFn ? () => wait().then(item.actionFn) : null, // do async to allow menu to close
|
|
74
|
+
popoverProps: {usePortal: true},
|
|
75
75
|
disabled: item.disabled,
|
|
76
76
|
items
|
|
77
77
|
});
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright Β© 2024 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
+
import {withDefault} from '@xh/hoist/utils/js';
|
|
8
|
+
import {isNil} from 'lodash';
|
|
7
9
|
import {useCallback, useEffect} from 'react';
|
|
8
10
|
import {CustomCellEditorProps, useGridCellEditor} from '@ag-grid-community/react';
|
|
9
11
|
import {hoistCmp} from '@xh/hoist/core';
|
|
@@ -22,6 +24,16 @@ export const [NumberEditor, numberEditor] = hoistCmp.withFactory<NumberEditorPro
|
|
|
22
24
|
observer: false,
|
|
23
25
|
render(props, ref) {
|
|
24
26
|
useNumberGuard(props.agParams);
|
|
27
|
+
|
|
28
|
+
// Make sure to override the NumberEditor debounce to 0 to prevent a bug where rapid changes are not saved.
|
|
29
|
+
if (isNil(props.inputProps)) props.inputProps = {};
|
|
30
|
+
// @ts-ignore
|
|
31
|
+
props.inputProps.commitOnChangeDebounce = withDefault(
|
|
32
|
+
// @ts-ignore
|
|
33
|
+
props.inputProps.commitOnChangeDebounce,
|
|
34
|
+
0
|
|
35
|
+
);
|
|
36
|
+
|
|
25
37
|
return useInlineEditorModel(numberInput, props, ref);
|
|
26
38
|
}
|
|
27
39
|
});
|
|
@@ -11,9 +11,9 @@ import '@xh/hoist/desktop/register';
|
|
|
11
11
|
import {fmtNumber, parseNumber} from '@xh/hoist/format';
|
|
12
12
|
import {numericInput} from '@xh/hoist/kit/blueprint';
|
|
13
13
|
import {wait} from '@xh/hoist/promise';
|
|
14
|
-
import {
|
|
14
|
+
import {TEST_ID, throwIf, withDefault} from '@xh/hoist/utils/js';
|
|
15
15
|
import {getLayoutProps} from '@xh/hoist/utils/react';
|
|
16
|
-
import {isNaN, isNil, isNumber, round} from 'lodash';
|
|
16
|
+
import {debounce, isNaN, isNil, isNumber, round} from 'lodash';
|
|
17
17
|
import {KeyboardEventHandler, ReactElement, ReactNode, Ref, useLayoutEffect} from 'react';
|
|
18
18
|
|
|
19
19
|
export interface NumberInputProps extends HoistProps, LayoutProps, StyleProps, HoistInputProps {
|
|
@@ -145,9 +145,12 @@ class NumberInputModel extends HoistInputModel {
|
|
|
145
145
|
this.noteValueChange(valAsString);
|
|
146
146
|
};
|
|
147
147
|
|
|
148
|
-
|
|
148
|
+
/** TODO: Completely remove the debounce, or find and verify a reason why we need it set to 250. */
|
|
149
149
|
override doCommitOnChangeInternal() {
|
|
150
|
-
|
|
150
|
+
debounce(
|
|
151
|
+
() => super.doCommitOnChangeInternal(),
|
|
152
|
+
withDefault(this.componentProps.commitOnChangeDebounce, 250)
|
|
153
|
+
)();
|
|
151
154
|
}
|
|
152
155
|
|
|
153
156
|
override toInternal(val: number): number {
|
|
@@ -50,7 +50,7 @@ export interface PanelProps extends HoistProps<PanelModel>, Omit<BoxProps, 'titl
|
|
|
50
50
|
/** Icon to be used when the panel is collapsed. Defaults to `icon`. */
|
|
51
51
|
collapsedIcon?: ReactElement;
|
|
52
52
|
|
|
53
|
-
/** Context
|
|
53
|
+
/** Context menu to show on a right-click within this panel. */
|
|
54
54
|
contextMenu?: ContextMenuSpec;
|
|
55
55
|
|
|
56
56
|
/**
|
|
@@ -6,17 +6,17 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import {contextMenu, ContextMenuSpec} from '@xh/hoist/desktop/cmp/contextmenu/ContextMenu';
|
|
8
8
|
import {showContextMenu} from '@xh/hoist/kit/blueprint';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {cloneElement, isValidElement} from 'react';
|
|
9
|
+
import {logError} from '@xh/hoist/utils/js';
|
|
10
|
+
import {isArray, isEmpty, isFunction, isUndefined} from 'lodash';
|
|
11
|
+
import {cloneElement, isValidElement, ReactElement} from 'react';
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* Hook to add context menu
|
|
14
|
+
* Hook to add a right-click context menu to a component.
|
|
15
15
|
*
|
|
16
|
-
* @param child - element to be given context menu support. Must specify
|
|
17
|
-
*
|
|
18
|
-
* @param spec -
|
|
19
|
-
*
|
|
16
|
+
* @param child - element to be given context menu support. Must specify a component that takes
|
|
17
|
+
* the React `onContextMenu` event as a prop (e.g. boxes, panel, div, etc.)
|
|
18
|
+
* @param spec - spec the menu to be shown. If null, or the number of items is empty, no menu will
|
|
19
|
+
* be rendered and the event will be consumed.
|
|
20
20
|
*/
|
|
21
21
|
export function useContextMenu(child?: ReactElement, spec?: ContextMenuSpec): ReactElement {
|
|
22
22
|
if (!child || isUndefined(spec)) return child;
|
|
@@ -24,11 +24,11 @@ export function useContextMenu(child?: ReactElement, spec?: ContextMenuSpec): Re
|
|
|
24
24
|
const onContextMenu = (e: MouseEvent) => {
|
|
25
25
|
let contextMenuOutput: any = spec;
|
|
26
26
|
|
|
27
|
-
// 0) Skip if already consumed, otherwise consume (
|
|
27
|
+
// 0) Skip if already consumed, otherwise consume (adapted from BP `ContextMenuTarget`).
|
|
28
28
|
if (e.defaultPrevented) return;
|
|
29
29
|
e.preventDefault();
|
|
30
30
|
|
|
31
|
-
// 1) Pre-process to an element (potentially via item list) or null
|
|
31
|
+
// 1) Pre-process to an element (potentially via item list) or null.
|
|
32
32
|
if (isFunction(contextMenuOutput)) {
|
|
33
33
|
contextMenuOutput = contextMenuOutput(e);
|
|
34
34
|
}
|
|
@@ -38,11 +38,11 @@ export function useContextMenu(child?: ReactElement, spec?: ContextMenuSpec): Re
|
|
|
38
38
|
: null;
|
|
39
39
|
}
|
|
40
40
|
if (contextMenuOutput && !isValidElement(contextMenuOutput)) {
|
|
41
|
-
|
|
41
|
+
logError(`Incorrect specification of 'contextMenu' arg in useContextMenu()`);
|
|
42
42
|
contextMenuOutput = null;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
// 2) Render via blueprint
|
|
45
|
+
// 2) Render via blueprint.
|
|
46
46
|
if (contextMenuOutput) {
|
|
47
47
|
showContextMenu(contextMenuOutput, {left: e.clientX, top: e.clientY});
|
|
48
48
|
}
|
|
@@ -10,9 +10,9 @@ import {fmtNumber} from '@xh/hoist/format';
|
|
|
10
10
|
import {input} from '@xh/hoist/kit/onsen';
|
|
11
11
|
import '@xh/hoist/mobile/register';
|
|
12
12
|
import {wait} from '@xh/hoist/promise';
|
|
13
|
-
import {
|
|
13
|
+
import {throwIf, withDefault} from '@xh/hoist/utils/js';
|
|
14
14
|
import {getLayoutProps} from '@xh/hoist/utils/react';
|
|
15
|
-
import {isNaN, isNil, isNumber, round} from 'lodash';
|
|
15
|
+
import {debounce, isNaN, isNil, isNumber, round} from 'lodash';
|
|
16
16
|
import './NumberInput.scss';
|
|
17
17
|
|
|
18
18
|
export interface NumberInputProps extends HoistProps, HoistInputProps, StyleProps, LayoutProps {
|
|
@@ -123,9 +123,12 @@ class NumberInputModel extends HoistInputModel {
|
|
|
123
123
|
this.noteValueChange(ev.target.value);
|
|
124
124
|
};
|
|
125
125
|
|
|
126
|
-
|
|
126
|
+
/** TODO: Completely remove the debounce, or find and verify a reason why we need it set to 250. */
|
|
127
127
|
override doCommitOnChangeInternal() {
|
|
128
|
-
|
|
128
|
+
debounce(
|
|
129
|
+
() => super.doCommitOnChangeInternal(),
|
|
130
|
+
withDefault(this.componentProps.commitOnChangeDebounce, 250)
|
|
131
|
+
)();
|
|
129
132
|
}
|
|
130
133
|
|
|
131
134
|
override toInternal(val) {
|