@gooddata/sdk-ui-ext 11.53.0-alpha.6 → 11.53.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.
- package/esm/internal/components/dialogs/userManagementDialogs/Workspace/AddSingleWorkspaceSelect.d.ts.map +1 -1
- package/esm/internal/components/dialogs/userManagementDialogs/Workspace/AddSingleWorkspaceSelect.js +1 -3
- package/esm/internal/translations/en-US.localization-bundle.d.ts +8 -0
- package/esm/internal/translations/en-US.localization-bundle.d.ts.map +1 -1
- package/esm/internal/translations/en-US.localization-bundle.js +8 -0
- package/esm/share/ObjectShareDialog.js +6 -4
- package/esm/share/messages.d.ts +11 -0
- package/esm/share/messages.d.ts.map +1 -1
- package/esm/share/messages.js +7 -0
- package/esm/share/objectShareController.types.d.ts +10 -8
- package/esm/share/objectShareController.types.d.ts.map +1 -1
- package/esm/share/useAccessList.d.ts +19 -1
- package/esm/share/useAccessList.d.ts.map +1 -1
- package/esm/share/useAccessList.js +20 -3
- package/esm/share/useObjectShareController.d.ts.map +1 -1
- package/esm/share/useObjectShareController.js +15 -10
- package/esm/share/useObjectShareDialog.d.ts +6 -2
- package/esm/share/useObjectShareDialog.d.ts.map +1 -1
- package/esm/share/useObjectShareDialog.js +33 -6
- package/package.json +21 -21
- package/esm/internal/components/pluggableVisualizations/geoChartNext/geoConfigBuilder.test.d.ts +0 -2
- package/esm/internal/components/pluggableVisualizations/geoChartNext/geoConfigBuilder.test.d.ts.map +0 -1
- package/esm/internal/components/pluggableVisualizations/geoChartNext/geoConfigBuilder.test.js +0 -415
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AddSingleWorkspaceSelect.d.ts","sourceRoot":"","sources":["../../../../../../src/internal/components/dialogs/userManagementDialogs/Workspace/AddSingleWorkspaceSelect.tsx"],"names":[],"mappings":"AAYA,OAAO,EACH,KAAK,8BAA8B,EAKtC,MAAM,aAAa,CAAC;AAgBrB,wBAAgB,wBAAwB,CAAC,EACrC,cAAc,EACd,iBAAiB,EACjB,iBAAiB,EACjB,IAAa,EAChB,EAAE,8BAA8B,
|
|
1
|
+
{"version":3,"file":"AddSingleWorkspaceSelect.d.ts","sourceRoot":"","sources":["../../../../../../src/internal/components/dialogs/userManagementDialogs/Workspace/AddSingleWorkspaceSelect.tsx"],"names":[],"mappings":"AAYA,OAAO,EACH,KAAK,8BAA8B,EAKtC,MAAM,aAAa,CAAC;AAgBrB,wBAAgB,wBAAwB,CAAC,EACrC,cAAc,EACd,iBAAiB,EACjB,iBAAiB,EACjB,IAAa,EAChB,EAAE,8BAA8B,2CA0GhC"}
|
package/esm/internal/components/dialogs/userManagementDialogs/Workspace/AddSingleWorkspaceSelect.js
CHANGED
|
@@ -63,9 +63,7 @@ export function AddSingleWorkspaceSelect({ addedWorkspace, grantedWorkspaces, on
|
|
|
63
63
|
}, styles: {
|
|
64
64
|
dropdownIndicator: (base) => ({
|
|
65
65
|
...base,
|
|
66
|
-
|
|
67
|
-
marginRight: "-6px",
|
|
68
|
-
svg: { height: "14px" },
|
|
66
|
+
svg: { width: "14px", height: "14px" },
|
|
69
67
|
}),
|
|
70
68
|
}, loadOptions: loadOptions, defaultOptions: true, placeholder: intl.formatMessage(messages.searchWorkspacePlaceholder), onKeyDown: onKeyDownCallback, noOptionsMessage: noOptionsMessage, onChange: onSelect, filterOption: filterOption, isMulti: false, isDisabled: !isEditMode, value: addedWorkspace
|
|
71
69
|
? {
|
|
@@ -2519,6 +2519,10 @@ export declare const en_US: {
|
|
|
2519
2519
|
text: string;
|
|
2520
2520
|
crowdinContext: string;
|
|
2521
2521
|
};
|
|
2522
|
+
"objectShare.granteeLevel.inheritedCovered": {
|
|
2523
|
+
text: string;
|
|
2524
|
+
crowdinContext: string;
|
|
2525
|
+
};
|
|
2522
2526
|
"objectShare.grantee.you": {
|
|
2523
2527
|
text: string;
|
|
2524
2528
|
crowdinContext: string;
|
|
@@ -2547,6 +2551,10 @@ export declare const en_US: {
|
|
|
2547
2551
|
text: string;
|
|
2548
2552
|
crowdinContext: string;
|
|
2549
2553
|
};
|
|
2554
|
+
"objectShare.toast.escalationRefused": {
|
|
2555
|
+
text: string;
|
|
2556
|
+
crowdinContext: string;
|
|
2557
|
+
};
|
|
2550
2558
|
"objectShare.toast.labelScopePartial": {
|
|
2551
2559
|
text: string;
|
|
2552
2560
|
crowdinContext: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"en-US.localization-bundle.d.ts","sourceRoot":"","sources":["../../../src/internal/translations/en-US.localization-bundle.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,KAAK
|
|
1
|
+
{"version":3,"file":"en-US.localization-bundle.d.ts","sourceRoot":"","sources":["../../../src/internal/translations/en-US.localization-bundle.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAivFjB,CAAC"}
|
|
@@ -2521,6 +2521,10 @@ export const en_US = {
|
|
|
2521
2521
|
"text": "This access is inherited from a group or a parent workspace and cannot be removed here.",
|
|
2522
2522
|
"crowdinContext": "Tooltip on the disabled Remove access action in a grantee row's permission menu, shown when the grantee has no permission granted in this workspace, so there is nothing here to remove."
|
|
2523
2523
|
},
|
|
2524
|
+
"objectShare.granteeLevel.inheritedCovered": {
|
|
2525
|
+
"text": "This level is already covered by access inherited from a group or a parent workspace.",
|
|
2526
|
+
"crowdinContext": "Tooltip on a disabled permission level in a grantee row's menu, shown when an inherited grant already covers that level, so picking it could not change what the grantee can do."
|
|
2527
|
+
},
|
|
2524
2528
|
"objectShare.grantee.you": {
|
|
2525
2529
|
"text": "{name} (you)",
|
|
2526
2530
|
"crowdinContext": "Display name of the signed-in user's own row in the share dialog grantee list. {name} is the user's full name."
|
|
@@ -2549,6 +2553,10 @@ export const en_US = {
|
|
|
2549
2553
|
"text": "Access could not be updated.",
|
|
2550
2554
|
"crowdinContext": "Toast shown when an access change fails."
|
|
2551
2555
|
},
|
|
2556
|
+
"objectShare.toast.escalationRefused": {
|
|
2557
|
+
"text": "You can only grant permissions that you have yourself.",
|
|
2558
|
+
"crowdinContext": "Error toast shown when a permission change was refused because the user tried to grant a higher permission level than they hold themselves."
|
|
2559
|
+
},
|
|
2552
2560
|
"objectShare.toast.labelScopePartial": {
|
|
2553
2561
|
"text": "Some label access could not be saved.",
|
|
2554
2562
|
"crowdinContext": "Warning toast when sharing an attribute succeeded but one or more of its per-label permission writes failed."
|
|
@@ -40,7 +40,7 @@ export function ObjectShareDialog(props) {
|
|
|
40
40
|
/** The dialog session — mounted only while open, so its state cannot outlive a close. */
|
|
41
41
|
function ObjectShareDialogSession({ target, objectTitle, onClose, onSummaryChange, labels, labelsLoading, labelsError, }) {
|
|
42
42
|
const intl = useIntl();
|
|
43
|
-
const { state, actions, isShareViewOpen, isAddGranteeOpen, isGeneralAccessConfirmOpen, isSelfRestrictConfirmOpen, isMutable, isLoading, workspaceDisabledLevels, rowDisabledLevels, isRowRemoveDisabled, rowRemoveDisabledTooltip, onClose: closeDialog, onRowPermissionChange, onRowRemove, onSelfRestrictCancel, onSelfRestrictConfirm, } = useObjectShareDialog({
|
|
43
|
+
const { state, actions, isShareViewOpen, isAddGranteeOpen, isGeneralAccessConfirmOpen, isSelfRestrictConfirmOpen, isMutable, isLoading, workspaceDisabledLevels, rowDisabledLevels, rowDisabledLevelTooltips, isRowRemoveDisabled, isRowControlsLocked, rowRemoveDisabledTooltip, onClose: closeDialog, onRowPermissionChange, onRowRemove, onSelfRestrictCancel, onSelfRestrictConfirm, } = useObjectShareDialog({
|
|
44
44
|
target,
|
|
45
45
|
onClose,
|
|
46
46
|
onSummaryChange,
|
|
@@ -81,13 +81,15 @@ function ObjectShareDialogSession({ target, objectTitle, onClose, onSummaryChang
|
|
|
81
81
|
: display.name,
|
|
82
82
|
email: display.email,
|
|
83
83
|
isPending: g.pending !== undefined,
|
|
84
|
-
controls: (_jsx(UiGranteeRowControls, { labels: rowLabelItems(g.id), selectedLabelIds: state.selectedLabelIdsByGrantee[g.id] ?? state.labels.map((l) => l.id), permissionLevel: g.level, effectivePermission: g.effectivePermission, disabledLevels: disabledLevels,
|
|
84
|
+
controls: (_jsx(UiGranteeRowControls, { labels: rowLabelItems(g.id), selectedLabelIds: state.selectedLabelIdsByGrantee[g.id] ?? state.labels.map((l) => l.id), permissionLevel: g.level, effectivePermission: g.effectivePermission, disabledLevels: disabledLevels,
|
|
85
|
+
// Row-wide warning is the self row's; covered picks carry their own.
|
|
86
|
+
disabledTooltip: state.selfManagedGranteeId === g.id
|
|
85
87
|
? intl.formatMessage(objectShareMessages.selfRestrictWarning)
|
|
86
|
-
: undefined, isRemoveDisabled: isRowRemoveDisabled(g), removeDisabledTooltip: rowRemoveDisabledTooltip,
|
|
88
|
+
: undefined, disabledLevelTooltips: rowDisabledLevelTooltips(g), isRemoveDisabled: isRowRemoveDisabled(g), removeDisabledTooltip: rowRemoveDisabledTooltip,
|
|
87
89
|
// Disabled while the row's own write is saving, while mutations are
|
|
88
90
|
// gated (unresolved label scope would orphan real per-label grants),
|
|
89
91
|
// and while a sole row's self identity is unknown.
|
|
90
|
-
isDisabled: g.pending !== undefined || !isMutable ||
|
|
92
|
+
isDisabled: g.pending !== undefined || !isMutable || isRowControlsLocked(g), onLabelsChange: (selectedIds) => {
|
|
91
93
|
void actions.changeGranteeLabels(g.id, selectedIds);
|
|
92
94
|
}, onPermissionChange: (level) => onRowPermissionChange(g, level), onRemoveAccess: () => onRowRemove(g) })),
|
|
93
95
|
};
|
package/esm/share/messages.d.ts
CHANGED
|
@@ -47,6 +47,17 @@ export declare const objectShareMessages: {
|
|
|
47
47
|
granteeRemoveInherited: {
|
|
48
48
|
id: string;
|
|
49
49
|
};
|
|
50
|
+
/** Disabled-level tooltip for picks an inherited grant already covers. */
|
|
51
|
+
granteeLevelInheritedCovered: {
|
|
52
|
+
id: string;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Error toast for a write the backend refused as an escalation. Distinct from the
|
|
56
|
+
* generic failure: the change was rejected on purpose, retrying will not help.
|
|
57
|
+
*/
|
|
58
|
+
toastEscalationRefused: {
|
|
59
|
+
id: string;
|
|
60
|
+
};
|
|
50
61
|
granteeYou: {
|
|
51
62
|
id: string;
|
|
52
63
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/share/messages.ts"],"names":[],"mappings":"AAIA;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;IAO5B;;;;OAIG;;;;IAEH;;;OAGG;;;;IAEH;;;OAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAWL,CAAC"}
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/share/messages.ts"],"names":[],"mappings":"AAIA;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;IAO5B;;;;OAIG;;;;IAEH;;;OAGG;;;;IAEH;;;OAGG;;;;IAEH,0EAA0E;;;;IAE1E;;;OAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAWL,CAAC"}
|
package/esm/share/messages.js
CHANGED
|
@@ -31,6 +31,13 @@ export const objectShareMessages = defineMessages({
|
|
|
31
31
|
* access is inherited only — there is no grant in this workspace to revoke.
|
|
32
32
|
*/
|
|
33
33
|
granteeRemoveInherited: { id: "objectShare.granteeRemove.inherited" },
|
|
34
|
+
/** Disabled-level tooltip for picks an inherited grant already covers. */
|
|
35
|
+
granteeLevelInheritedCovered: { id: "objectShare.granteeLevel.inheritedCovered" },
|
|
36
|
+
/**
|
|
37
|
+
* Error toast for a write the backend refused as an escalation. Distinct from the
|
|
38
|
+
* generic failure: the change was rejected on purpose, retrying will not help.
|
|
39
|
+
*/
|
|
40
|
+
toastEscalationRefused: { id: "objectShare.toast.escalationRefused" },
|
|
34
41
|
granteeYou: { id: "objectShare.grantee.you" },
|
|
35
42
|
adminTagLabel: { id: "objectShare.adminTag.label" },
|
|
36
43
|
adminTagTooltip: { id: "objectShare.adminTag.tooltip" },
|
|
@@ -90,10 +90,11 @@ export interface IObjectShareControllerState {
|
|
|
90
90
|
summary: IObjectAccessSummary | undefined;
|
|
91
91
|
grantees: IObjectShareGrantee[];
|
|
92
92
|
/**
|
|
93
|
-
* Id of the grantee row whose permission menu manages the signed-in user's
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
93
|
+
* Id of the grantee row whose permission menu manages the signed-in user's OWN
|
|
94
|
+
* access (the "restrict own access" design), whatever else is listed. Consumers
|
|
95
|
+
* gate lowering behind a confirm and disable the levels above the row's own.
|
|
96
|
+
* Undefined for a workspace manager (their access is not this grant) and while
|
|
97
|
+
* their identity is unresolved, which `granteeControlsLocked` covers.
|
|
97
98
|
*/
|
|
98
99
|
selfManagedGranteeId: string | undefined;
|
|
99
100
|
/**
|
|
@@ -110,10 +111,11 @@ export interface IObjectShareControllerState {
|
|
|
110
111
|
*/
|
|
111
112
|
workspaceDisabledLevels: ObjectSharePermissionLevel[] | undefined;
|
|
112
113
|
/**
|
|
113
|
-
* Whether
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
114
|
+
* Whether USER-row controls must stay disabled because no row can be told apart
|
|
115
|
+
* from the caller's own grant yet (profile pending or silently failed) — mutating
|
|
116
|
+
* one then would bypass the self-restriction confirm and can lose access for good.
|
|
117
|
+
* Set however many rows are listed. Group rows are never the caller, and a known
|
|
118
|
+
* workspace manager needs no confirm, so neither sets this.
|
|
117
119
|
*/
|
|
118
120
|
granteeControlsLocked: boolean;
|
|
119
121
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"objectShareController.types.d.ts","sourceRoot":"","sources":["../../src/share/objectShareController.types.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC5E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAEzG,OAAO,KAAK,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAE1E;;;;;GAKG;AACH,MAAM,MAAM,0BAA0B,GAAG,wBAAwB,CAAC;AAElE;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IAClC,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAc,SAAQ,qBAAqB;IACxD,EAAE,EAAE,MAAM,CAAC;CACd;AAED;;;;;GAKG;AACH,MAAM,WAAW,mBAAoB,SAAQ,qBAAqB;IAC9D,kFAAkF;IAClF,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,KAAK,EAAE,0BAA0B,CAAC;IAClC;;;;;OAKG;IACH,WAAW,CAAC,EAAE,0BAA0B,CAAC;IACzC;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,0BAA0B,CAAC;IACjD;;;;;OAKG;IACH,cAAc,CAAC,EAAE,0BAA0B,CAAC;IAC5C;;;;OAIG;IACH,OAAO,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,2BAA2B;IACxC,OAAO,EAAE,MAAM,GAAG,YAAY,CAAC;IAC/B,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;IAC5D,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB,OAAO,EAAE,oBAAoB,GAAG,SAAS,CAAC;IAC1C,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC
|
|
1
|
+
{"version":3,"file":"objectShareController.types.d.ts","sourceRoot":"","sources":["../../src/share/objectShareController.types.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC5E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAEzG,OAAO,KAAK,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAE1E;;;;;GAKG;AACH,MAAM,MAAM,0BAA0B,GAAG,wBAAwB,CAAC;AAElE;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IAClC,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAc,SAAQ,qBAAqB;IACxD,EAAE,EAAE,MAAM,CAAC;CACd;AAED;;;;;GAKG;AACH,MAAM,WAAW,mBAAoB,SAAQ,qBAAqB;IAC9D,kFAAkF;IAClF,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,KAAK,EAAE,0BAA0B,CAAC;IAClC;;;;;OAKG;IACH,WAAW,CAAC,EAAE,0BAA0B,CAAC;IACzC;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,0BAA0B,CAAC;IACjD;;;;;OAKG;IACH,cAAc,CAAC,EAAE,0BAA0B,CAAC;IAC5C;;;;OAIG;IACH,OAAO,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,2BAA2B;IACxC,OAAO,EAAE,MAAM,GAAG,YAAY,CAAC;IAC/B,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;IAC5D,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB,OAAO,EAAE,oBAAoB,GAAG,SAAS,CAAC;IAC1C,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC;;;;;;OAMG;IACH,oBAAoB,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC;;;;;OAKG;IACH,yBAAyB,EAAE,0BAA0B,EAAE,GAAG,SAAS,CAAC;IACpE;;;;OAIG;IACH,uBAAuB,EAAE,0BAA0B,EAAE,GAAG,SAAS,CAAC;IAClE;;;;;;OAMG;IACH,qBAAqB,EAAE,OAAO,CAAC;IAC/B;;;;;;;;OAQG;IACH,YAAY,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;IAC3D,aAAa,EAAE,kBAAkB,CAAC;IAClC;;;;OAIG;IACH,cAAc,EAAE,0BAA0B,CAAC;IAC3C;;;;;;OAMG;IACH,uBAAuB,EAAE,0BAA0B,GAAG,SAAS,CAAC;IAChE;;;;OAIG;IACH,oBAAoB,EAAE,OAAO,CAAC;IAC9B;;;;OAIG;IACH,oBAAoB,EAAE,OAAO,CAAC;IAE9B;;;OAGG;IACH,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B;;;;;OAKG;IACH,cAAc,EAAE,OAAO,CAAC;IACxB;;;;;;OAMG;IACH,kBAAkB,EAAE,OAAO,CAAC;IAC5B;;;;;;;OAOG;IACH,yBAAyB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACpD;;;;;;OAMG;IACH,0BAA0B,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAErD,mEAAiE;IACjE,oBAAoB,CAAC,EAAE,kBAAkB,CAAC;IAC1C,qEAAqE;IACrE,eAAe,EAAE,gBAAgB,EAAE,CAAC;CACvC;AAED;;GAEG;AACH,MAAM,WAAW,6BAA6B;IAC1C,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,kBAAkB,EAAE,CAAC,IAAI,EAAE,gBAAgB,EAAE,KAAK,IAAI,CAAC;IACvD;;;;OAIG;IACH,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,sBAAsB,CAAC,CAAC;IACjE,kDAAkD;IAClD,kBAAkB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAExC,oEAAoE;IACpE,qBAAqB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,0BAA0B,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/F,oCAAoC;IACpC,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD;;;OAGG;IACH,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAEtF,+DAA+D;IAC/D,0BAA0B,EAAE,CAAC,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAC/D,yBAAyB,EAAE,MAAM,IAAI,CAAC;IACtC,4DAA4D;IAC5D,0BAA0B,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD;;;;;OAKG;IACH,oBAAoB,EAAE,CAAC,KAAK,EAAE,0BAA0B,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9E;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACnC,KAAK,EAAE,2BAA2B,CAAC;IACnC,OAAO,EAAE,6BAA6B,CAAC;CAC1C;AAED;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACnC;;;OAGG;IACH,MAAM,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAC7B;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CAC3B"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type IObjectPermissionsObject } from "@gooddata/sdk-backend-spi";
|
|
2
2
|
import { type IGranularAccessGrantee } from "@gooddata/sdk-model";
|
|
3
3
|
import { type GoodDataSdkError } from "@gooddata/sdk-ui";
|
|
4
4
|
import { type GeneralAccessValue, type IUiGranteeAsyncOptions } from "@gooddata/sdk-ui-kit";
|
|
@@ -23,8 +23,26 @@ export interface IAccessList {
|
|
|
23
23
|
* way in must not read as grant-independent after locally removing it.
|
|
24
24
|
*/
|
|
25
25
|
seededWithoutSelfGrant: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Whether the SEED's workspace rule was share-capable (SHARE or EDIT, own or
|
|
28
|
+
* inherited). Load-time fact like {@link seededWithoutSelfGrant}: the Admin badge
|
|
29
|
+
* infers how the caller ENTERED, so restricting the rule mid-session must not
|
|
30
|
+
* start reading as admin access.
|
|
31
|
+
*/
|
|
32
|
+
seededRuleShareCapable: boolean;
|
|
26
33
|
/** Signed-in user's identity facts + login id, once the profile resolves. */
|
|
27
34
|
selfIdentity: ISelfIdentity | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Whether the caller manages this workspace (org admins arrive through the same
|
|
37
|
+
* MANAGE permission), or **undefined** while the read is pending or after it failed.
|
|
38
|
+
*
|
|
39
|
+
* Read it in the direction the policy needs. A protective policy (the
|
|
40
|
+
* self-restriction confirm, the row lock) may treat undefined as "not a manager",
|
|
41
|
+
* since a needless confirm is harmless. A restrictive one (a limit on what may be
|
|
42
|
+
* granted) must require an explicit `false`, or a failed read would block a caller
|
|
43
|
+
* the backend would allow.
|
|
44
|
+
*/
|
|
45
|
+
isWorkspaceManager: boolean | undefined;
|
|
28
46
|
/**
|
|
29
47
|
* Whether the profile resolved. Until then a sole grantee row can't be told apart
|
|
30
48
|
* from the caller's own grant, so its `isSelf` is only an unresolved default.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAccessList.d.ts","sourceRoot":"","sources":["../../src/share/useAccessList.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"useAccessList.d.ts","sourceRoot":"","sources":["../../src/share/useAccessList.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,wBAAwB,EAAiC,MAAM,2BAA2B,CAAC;AACzG,OAAO,EACH,KAAK,sBAAsB,EAM9B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACH,KAAK,gBAAgB,EAKxB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,KAAK,kBAAkB,EAAE,KAAK,sBAAsB,EAAmB,MAAM,sBAAsB,CAAC;AAU7G,OAAO,EAEH,KAAK,SAAS,EASjB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,KAAK,EACR,2BAA2B,EAC3B,mBAAmB,EACnB,aAAa,EACb,0BAA0B,EAC7B,MAAM,kCAAkC,CAAC;AAC1C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD;;;;GAIG;AACH,MAAM,WAAW,WAAW;IACxB,oDAAoD;IACpD,OAAO,EAAE,OAAO,CAAC;IACjB,2EAA2E;IAC3E,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC;;;;;;OAMG;IACH,sBAAsB,EAAE,OAAO,CAAC;IAChC;;;;;OAKG;IACH,sBAAsB,EAAE,OAAO,CAAC;IAChC,6EAA6E;IAC7E,YAAY,EAAE,aAAa,GAAG,SAAS,CAAC;IACxC;;;;;;;;;OASG;IACH,kBAAkB,EAAE,OAAO,GAAG,SAAS,CAAC;IACxC;;;OAGG;IACH,oBAAoB,EAAE,OAAO,CAAC;IAC9B,8DAA8D;IAC9D,aAAa,EAAE,kBAAkB,CAAC;IAClC,oFAAkF;IAClF,cAAc,EAAE,0BAA0B,CAAC;IAC3C;;;OAGG;IACH,uBAAuB,EAAE,0BAA0B,GAAG,SAAS,CAAC;IAChE,wFAAwF;IACxF,oBAAoB,EAAE,OAAO,CAAC;IAC9B,iFAAiF;IACjF,OAAO,EAAE,oBAAoB,GAAG,SAAS,CAAC;IAC1C,qDAAqD;IACrD,MAAM,EAAE,2BAA2B,CAAC,QAAQ,CAAC,CAAC;IAC9C,mDAAmD;IACnD,SAAS,EAAE,gBAAgB,GAAG,SAAS,CAAC;IAExC,mFAAmF;IACnF,MAAM,EAAE,CAAC,MAAM,EAAE,sBAAsB,EAAE,EAAE,cAAc,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/F,+FAA6F;IAC7F,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAEjE;;;;OAIG;IACH,eAAe,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACxD;;;;OAIG;IACH,iBAAiB,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,0BAA0B,KAAK,IAAI,CAAC;IAC3E;;;;OAIG;IACH,gBAAgB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,gFAAgF;IAChF,kBAAkB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,iEAAiE;IACjE,iBAAiB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC;;;OAGG;IACH,eAAe,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAEtC,oFAAoF;IACpF,aAAa,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,KAAK,IAAI,CAAC;IAC1D,6DAA6D;IAC7D,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,8FAA8F;IAC9F,YAAY,EAAE,MAAM,IAAI,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,wBAAwB,GAAG,SAAS,GAAG,WAAW,CAsUvF"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// (C) 2026 GoodData Corporation
|
|
2
2
|
import { useCallback, useMemo, useState } from "react";
|
|
3
|
+
import { isPermissionEscalationRefused } from "@gooddata/sdk-backend-spi";
|
|
3
4
|
import { idRef, objRefToString, serializeObjRef, } from "@gooddata/sdk-model";
|
|
4
5
|
import { convertError, useBackendStrict, useCancelablePromise, useWorkspaceStrict, } from "@gooddata/sdk-ui";
|
|
5
6
|
import { useToastMessage } from "@gooddata/sdk-ui-kit";
|
|
6
|
-
import { composeEffectiveWorkspaceAccess, deriveGeneralAccess, deriveInheritedWorkspaceLevel, deriveWorkspacePermissionLevel, } from "./accessSummary.js";
|
|
7
|
+
import { accessListToSummary, composeEffectiveWorkspaceAccess, deriveGeneralAccess, deriveInheritedWorkspaceLevel, deriveWorkspacePermissionLevel, } from "./accessSummary.js";
|
|
7
8
|
import { objectShareMessages } from "./messages.js";
|
|
8
9
|
import { assigneeIdentityFacts, assigneeMatchesQuery, granteeId, granteesFromAccessList, mergeGrantees, userDisplayPair, userIdentityFacts, withDirectLevel, } from "./objectShareController.helpers.js";
|
|
9
10
|
/**
|
|
@@ -51,10 +52,21 @@ export function useAccessList(target) {
|
|
|
51
52
|
// Success only: while pending or after a swallowed failure a sole row can't be told
|
|
52
53
|
// apart from the caller's own grant, so it must not be treated as safe to mutate.
|
|
53
54
|
const selfIdentityResolved = currentUserStatus === "success";
|
|
55
|
+
// Read straight from the backend, like the profile above, and keyed on it: the shared
|
|
56
|
+
// per-workspace loader caches one answer for the workspace, so after a backend swap it
|
|
57
|
+
// would hand back the PREVIOUS user's permissions and the policies below would trust it.
|
|
58
|
+
const { result: workspacePermissions } = useCancelablePromise({
|
|
59
|
+
promise: async () => backend.workspace(workspace).permissions().getPermissionsForCurrentUser(),
|
|
60
|
+
onError: () => { },
|
|
61
|
+
}, [backend, workspace]);
|
|
62
|
+
const isWorkspaceManager = workspacePermissions?.canManageProject;
|
|
54
63
|
const selfId = currentUser ? granteeId("user", idRef(currentUser.login)) : undefined;
|
|
55
64
|
// Derived from the immutable SEED (see the interface doc): a caller whose own
|
|
56
65
|
// grant was the way in must not read as grant-independent after removing it.
|
|
57
66
|
const seededWithoutSelfGrant = hasList && selfIdentityResolved && !base.some((g) => g.id === selfId);
|
|
67
|
+
// Seed only, never the rule overlay; `accessListToSummary` so this cannot drift.
|
|
68
|
+
const seededSummary = hasList ? accessListToSummary(fetchedList) : undefined;
|
|
69
|
+
const seededRuleShareCapable = seededSummary?.generalAccess === "WORKSPACE" && seededSummary.workspaceLevel !== "VIEW";
|
|
58
70
|
// De-collapsed like every listing fact — on tiger the user id is often the email.
|
|
59
71
|
const selfIdentity = useMemo(() => currentUser
|
|
60
72
|
? {
|
|
@@ -106,8 +118,11 @@ export function useAccessList(target) {
|
|
|
106
118
|
toast.addSuccess(successMessage);
|
|
107
119
|
return true;
|
|
108
120
|
}
|
|
109
|
-
catch {
|
|
110
|
-
|
|
121
|
+
catch (error) {
|
|
122
|
+
// A refused escalation is a deliberate "no", not a failure to retry.
|
|
123
|
+
toast.addError(isPermissionEscalationRefused(error)
|
|
124
|
+
? objectShareMessages.toastEscalationRefused
|
|
125
|
+
: objectShareMessages.toastError);
|
|
111
126
|
return false;
|
|
112
127
|
}
|
|
113
128
|
}, [backend, workspace, target, toast]);
|
|
@@ -244,7 +259,9 @@ export function useAccessList(target) {
|
|
|
244
259
|
hasList,
|
|
245
260
|
grantees,
|
|
246
261
|
seededWithoutSelfGrant,
|
|
262
|
+
seededRuleShareCapable,
|
|
247
263
|
selfIdentity,
|
|
264
|
+
isWorkspaceManager,
|
|
248
265
|
selfIdentityResolved,
|
|
249
266
|
generalAccess,
|
|
250
267
|
workspaceLevel,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useObjectShareController.d.ts","sourceRoot":"","sources":["../../src/share/useObjectShareController.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAkB1E,OAAO,EACH,KAAK,sBAAsB,EAI3B,KAAK,sBAAsB,EAE9B,MAAM,kCAAkC,CAAC;AAI1C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,wBAAwB,CACpC,MAAM,EAAE,wBAAwB,GAAG,SAAS,EAC5C,OAAO,CAAC,EAAE,sBAAsB,GACjC,sBAAsB,
|
|
1
|
+
{"version":3,"file":"useObjectShareController.d.ts","sourceRoot":"","sources":["../../src/share/useObjectShareController.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAkB1E,OAAO,EACH,KAAK,sBAAsB,EAI3B,KAAK,sBAAsB,EAE9B,MAAM,kCAAkC,CAAC;AAI1C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,wBAAwB,CACpC,MAAM,EAAE,wBAAwB,GAAG,SAAS,EAC5C,OAAO,CAAC,EAAE,sBAAsB,GACjC,sBAAsB,CAwxBxB"}
|
|
@@ -35,7 +35,7 @@ export function useObjectShareController(target, options) {
|
|
|
35
35
|
const [subview, setSubview] = useState("main");
|
|
36
36
|
const [pendingGrantees, setPendingGrantees] = useState([]);
|
|
37
37
|
const [pendingGeneralAccess, setPendingGeneralAccess] = useState(undefined);
|
|
38
|
-
const { hasList, grantees, seededWithoutSelfGrant, selfIdentity, selfIdentityResolved, generalAccess, workspaceLevel, workspaceInheritedLevel, workspaceLevelSaving, summary, status, loadError, commit, loadOptions, applyGranteeAdd, applyGranteeLevel, applyGranteeLock, applyGranteeRemove, settleGranteeEdit, failGranteeEdit, applyRuleEdit, settleRuleEdit, failRuleEdit, } = useAccessList(target);
|
|
38
|
+
const { hasList, grantees, seededWithoutSelfGrant, seededRuleShareCapable, selfIdentity, isWorkspaceManager, selfIdentityResolved, generalAccess, workspaceLevel, workspaceInheritedLevel, workspaceLevelSaving, summary, status, loadError, commit, loadOptions, applyGranteeAdd, applyGranteeLevel, applyGranteeLock, applyGranteeRemove, settleGranteeEdit, failGranteeEdit, applyRuleEdit, settleRuleEdit, failRuleEdit, } = useAccessList(target);
|
|
39
39
|
// Grantee ids the label-scope probe resolves against — a removing row is excluded
|
|
40
40
|
// so its scope is dropped, not re-seeded.
|
|
41
41
|
const committedGranteeIds = useMemo(() => {
|
|
@@ -605,20 +605,23 @@ export function useObjectShareController(target, options) {
|
|
|
605
605
|
const state = useMemo(() => {
|
|
606
606
|
// Row policy is classified here, not in the dialog, so a consumer
|
|
607
607
|
// injecting this controller into its own UI gets the same classification.
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
//
|
|
611
|
-
|
|
612
|
-
const
|
|
608
|
+
// The policy follows the self row however many others are listed: what makes
|
|
609
|
+
// it special is that the access is THEIRS. A manager is exempt, their access
|
|
610
|
+
// comes from the role, so there is no ceiling to cap and no lockout to warn of.
|
|
611
|
+
const selfRow = isWorkspaceManager ? undefined : grantees.find((g) => g.isSelf);
|
|
612
|
+
const selfManagedGranteeId = selfRow?.id;
|
|
613
|
+
const granteeControlsLocked = !selfIdentityResolved && !isWorkspaceManager && grantees.some((g) => g.kind === "user");
|
|
613
614
|
// The synthesized Admin row shows only while NO other permissions are set:
|
|
614
615
|
// the caller must have reached the list without a grant of their own
|
|
615
616
|
// (`seededWithoutSelfGrant` — a non-admin who locally removes their own
|
|
616
617
|
// grant must not gain the badge) and the list must currently be empty, so
|
|
617
618
|
// adding a grantee hides it and removing the last one brings it back.
|
|
619
|
+
// A share-capable rule also passes the gate, judged from the SEED. A group
|
|
620
|
+
// grant is another way in this cannot see: the heuristic's accepted blind spot.
|
|
618
621
|
const adminSelfRow = status === "success" &&
|
|
619
622
|
seededWithoutSelfGrant &&
|
|
620
623
|
grantees.length === 0 &&
|
|
621
|
-
!
|
|
624
|
+
!seededRuleShareCapable &&
|
|
622
625
|
selfIdentity
|
|
623
626
|
? userDisplayPair(selfIdentity, selfIdentity.id)
|
|
624
627
|
: undefined;
|
|
@@ -629,9 +632,9 @@ export function useObjectShareController(target, options) {
|
|
|
629
632
|
summary,
|
|
630
633
|
grantees: sortedGrantees,
|
|
631
634
|
selfManagedGranteeId,
|
|
632
|
-
// Policy, not just classification: the caller's own
|
|
633
|
-
//
|
|
634
|
-
selfManagedDisabledLevels:
|
|
635
|
+
// Policy, not just classification: the caller's own row can't be raised
|
|
636
|
+
// above itself, an inherited workspace level can't be undercut.
|
|
637
|
+
selfManagedDisabledLevels: selfRow ? levelsAbove(selfRow.level) : undefined,
|
|
635
638
|
workspaceDisabledLevels: workspaceInheritedLevel
|
|
636
639
|
? levelsBelow(workspaceInheritedLevel)
|
|
637
640
|
: undefined,
|
|
@@ -656,7 +659,9 @@ export function useObjectShareController(target, options) {
|
|
|
656
659
|
summary,
|
|
657
660
|
selfIdentity,
|
|
658
661
|
selfIdentityResolved,
|
|
662
|
+
isWorkspaceManager,
|
|
659
663
|
seededWithoutSelfGrant,
|
|
664
|
+
seededRuleShareCapable,
|
|
660
665
|
grantees,
|
|
661
666
|
sortedGrantees,
|
|
662
667
|
effectiveWorkspace,
|
|
@@ -40,10 +40,12 @@ export interface IObjectShareDialogViewModel {
|
|
|
40
40
|
/** The controller's workspace-rule menu policy, forwarded (see `IObjectShareControllerState`). */
|
|
41
41
|
workspaceDisabledLevels: ObjectSharePermissionLevel[] | undefined;
|
|
42
42
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
43
|
+
* Level picks a row must render disabled: the self-managed-row policy plus every
|
|
44
|
+
* pick an inherited grant already covers.
|
|
45
45
|
*/
|
|
46
46
|
rowDisabledLevels: (grantee: IObjectShareGrantee) => ObjectSharePermissionLevel[] | undefined;
|
|
47
|
+
/** Per-level tooltips for the disabled picks an inherited grant covers. */
|
|
48
|
+
rowDisabledLevelTooltips: (grantee: IObjectShareGrantee) => Partial<Record<ObjectSharePermissionLevel, string>> | undefined;
|
|
47
49
|
/**
|
|
48
50
|
* Whether Remove access must render disabled: the grantee holds no grant in this
|
|
49
51
|
* workspace, so there is nothing here to revoke (their access is inherited).
|
|
@@ -51,6 +53,8 @@ export interface IObjectShareDialogViewModel {
|
|
|
51
53
|
isRowRemoveDisabled: (grantee: IObjectShareGrantee) => boolean;
|
|
52
54
|
/** Tooltip explaining why Remove access is disabled on this row. */
|
|
53
55
|
rowRemoveDisabledTooltip: string;
|
|
56
|
+
/** Whether this row's controls must stay disabled (see `granteeControlsLocked`). */
|
|
57
|
+
isRowControlsLocked: (grantee: IObjectShareGrantee) => boolean;
|
|
54
58
|
/** Close the whole dialog, discarding any staged self-restriction. */
|
|
55
59
|
onClose: () => void;
|
|
56
60
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useObjectShareDialog.d.ts","sourceRoot":"","sources":["../../src/share/useObjectShareDialog.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"useObjectShareDialog.d.ts","sourceRoot":"","sources":["../../src/share/useObjectShareDialog.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAQ1E,OAAO,EACH,KAAK,6BAA6B,EAClC,KAAK,2BAA2B,EAChC,KAAK,mBAAmB,EACxB,KAAK,0BAA0B,EAClC,MAAM,kCAAkC,CAAC;AAC1C,OAAO,KAAK,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAsB1E;;;;;;;GAOG;AACH,MAAM,WAAW,2BAA2B;IACxC,yFAAyF;IACzF,KAAK,EAAE,2BAA2B,CAAC;IACnC,yEAAyE;IACzE,OAAO,EAAE,6BAA6B,CAAC;IAEvC,8EAA8E;IAC9E,eAAe,EAAE,OAAO,CAAC;IACzB,qDAAqD;IACrD,gBAAgB,EAAE,OAAO,CAAC;IAC1B,4EAA0E;IAC1E,0BAA0B,EAAE,OAAO,CAAC;IACpC,+EAA+E;IAC/E,yBAAyB,EAAE,OAAO,CAAC;IAEnC;;;;;OAKG;IACH,SAAS,EAAE,OAAO,CAAC;IACnB;;;;;;OAMG;IACH,SAAS,EAAE,OAAO,CAAC;IAEnB,kGAAkG;IAClG,uBAAuB,EAAE,0BAA0B,EAAE,GAAG,SAAS,CAAC;IAClE;;;OAGG;IACH,iBAAiB,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,0BAA0B,EAAE,GAAG,SAAS,CAAC;IAC9F,2EAA2E;IAC3E,wBAAwB,EAAE,CACtB,OAAO,EAAE,mBAAmB,KAC3B,OAAO,CAAC,MAAM,CAAC,0BAA0B,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,CAAC;IACrE;;;OAGG;IACH,mBAAmB,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,OAAO,CAAC;IAC/D,oEAAoE;IACpE,wBAAwB,EAAE,MAAM,CAAC;IACjC,oFAAoF;IACpF,mBAAmB,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,OAAO,CAAC;IAE/D,sEAAsE;IACtE,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB;;;OAGG;IACH,qBAAqB,EAAE,CAAC,OAAO,EAAE,mBAAmB,EAAE,KAAK,EAAE,0BAA0B,KAAK,IAAI,CAAC;IACjG,qGAAqG;IACrG,WAAW,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACpD,qFAAqF;IACrF,oBAAoB,EAAE,MAAM,IAAI,CAAC;IACjC,+FAA6F;IAC7F,qBAAqB,EAAE,MAAM,IAAI,CAAC;CACrC;AAED;;;;GAIG;AACH,UAAU,2BAA2B;IACjC,wFAAsF;IACtF,MAAM,EAAE,wBAAwB,GAAG,SAAS,CAAC;IAC7C,oGAAoG;IACpG,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,mFAAmF;IACnF,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAC1D,2FAA2F;IAC3F,MAAM,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAC7B,oGAAoG;IACpG,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,iGAAiG;IACjG,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,EACjC,MAAM,EACN,OAAO,EACP,eAAe,EACf,MAAM,EACN,aAAa,EACb,WAAW,EACd,EAAE,2BAA2B,GAAG,2BAA2B,CAwI3D"}
|
|
@@ -2,8 +2,17 @@
|
|
|
2
2
|
import { useCallback, useEffect, useState } from "react";
|
|
3
3
|
import { useIntl } from "react-intl";
|
|
4
4
|
import { objectShareMessages } from "./messages.js";
|
|
5
|
-
import { changesEffectiveLevel, removalChangesEffectiveLevel } from "./objectShareController.helpers.js";
|
|
5
|
+
import { changesEffectiveLevel, levelsBelow, removalChangesEffectiveLevel, } from "./objectShareController.helpers.js";
|
|
6
6
|
import { useObjectShareController } from "./useObjectShareController.js";
|
|
7
|
+
/**
|
|
8
|
+
* Level picks an inherited grant already covers, which the controller refuses as
|
|
9
|
+
* no-ops ({@link changesEffectiveLevel}). Lowering a direct grant that sits ABOVE
|
|
10
|
+
* what is inherited is NOT one of them: that does move the effective level (direct
|
|
11
|
+
* EDIT under inherited SHARE, picking VIEW yields SHARE).
|
|
12
|
+
*/
|
|
13
|
+
function inheritedCoveredLevels(grantee) {
|
|
14
|
+
return removalChangesEffectiveLevel(grantee) ? [] : levelsBelow(grantee.level);
|
|
15
|
+
}
|
|
7
16
|
/**
|
|
8
17
|
* Owns everything {@link ObjectShareDialog} needs to render for one dialog session:
|
|
9
18
|
* the controller, the self-restriction confirm flow, the mutation-gating flags, and
|
|
@@ -72,12 +81,28 @@ export function useObjectShareDialog({ target, onClose, onSummaryChange, labels,
|
|
|
72
81
|
}
|
|
73
82
|
}, [pendingSelfChange, actions]);
|
|
74
83
|
const onSelfRestrictCancel = useCallback(() => setPendingSelfChange(undefined), []);
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
84
|
+
const inheritedCoveredTooltip = intl.formatMessage(objectShareMessages.granteeLevelInheritedCovered);
|
|
85
|
+
const rowDisabledLevels = useCallback((grantee) => {
|
|
86
|
+
const self = state.selfManagedGranteeId === grantee.id ? (state.selfManagedDisabledLevels ?? []) : [];
|
|
87
|
+
// Disjoint: self caps levels ABOVE the row, coverage disables those BELOW.
|
|
88
|
+
const merged = [...self, ...inheritedCoveredLevels(grantee)];
|
|
89
|
+
return merged.length > 0 ? merged : undefined;
|
|
90
|
+
}, [state.selfManagedGranteeId, state.selfManagedDisabledLevels]);
|
|
91
|
+
const rowDisabledLevelTooltips = useCallback((grantee) => {
|
|
92
|
+
const covered = inheritedCoveredLevels(grantee);
|
|
93
|
+
if (covered.length === 0) {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
const map = {};
|
|
97
|
+
for (const level of covered) {
|
|
98
|
+
map[level] = inheritedCoveredTooltip;
|
|
99
|
+
}
|
|
100
|
+
return map;
|
|
101
|
+
}, [inheritedCoveredTooltip]);
|
|
80
102
|
const isRowRemoveDisabled = useCallback((grantee) => grantee.directLevel === undefined, []);
|
|
103
|
+
// Only USER rows can be the caller's own, so a group row stays usable while the
|
|
104
|
+
// profile is unresolved.
|
|
105
|
+
const isRowControlsLocked = useCallback((grantee) => state.granteeControlsLocked && grantee.kind === "user", [state.granteeControlsLocked]);
|
|
81
106
|
const isMutable = state.status === "success" && state.labelsResolved;
|
|
82
107
|
const isLoading = state.status === "loading" || state.labelsInitializing;
|
|
83
108
|
return {
|
|
@@ -91,7 +116,9 @@ export function useObjectShareDialog({ target, onClose, onSummaryChange, labels,
|
|
|
91
116
|
isLoading,
|
|
92
117
|
workspaceDisabledLevels: state.workspaceDisabledLevels,
|
|
93
118
|
rowDisabledLevels,
|
|
119
|
+
rowDisabledLevelTooltips,
|
|
94
120
|
isRowRemoveDisabled,
|
|
121
|
+
isRowControlsLocked,
|
|
95
122
|
rowRemoveDisabledTooltip: intl.formatMessage(objectShareMessages.granteeRemoveInherited),
|
|
96
123
|
onClose,
|
|
97
124
|
onRowPermissionChange,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gooddata/sdk-ui-ext",
|
|
3
|
-
"version": "11.53.0
|
|
3
|
+
"version": "11.53.0",
|
|
4
4
|
"description": "GoodData.UI SDK - Extensions",
|
|
5
5
|
"license": "LicenseRef-LICENSE",
|
|
6
6
|
"author": "GoodData Corporation",
|
|
@@ -63,20 +63,20 @@
|
|
|
63
63
|
"ts-invariant": "0.10.3",
|
|
64
64
|
"tslib": "2.8.1",
|
|
65
65
|
"uuid": "11.1.1",
|
|
66
|
-
"@gooddata/sdk-backend-
|
|
67
|
-
"@gooddata/sdk-
|
|
68
|
-
"@gooddata/sdk-
|
|
69
|
-
"@gooddata/sdk-
|
|
70
|
-
"@gooddata/sdk-ui": "11.53.0
|
|
71
|
-
"@gooddata/sdk-ui-
|
|
72
|
-
"@gooddata/sdk-ui-geo": "11.53.0
|
|
73
|
-
"@gooddata/sdk-ui-
|
|
74
|
-
"@gooddata/sdk-ui-
|
|
75
|
-
"@gooddata/sdk-ui-
|
|
76
|
-
"@gooddata/sdk-ui-
|
|
77
|
-
"@gooddata/sdk-ui-
|
|
78
|
-
"@gooddata/sdk-ui-
|
|
79
|
-
"@gooddata/util": "11.53.0
|
|
66
|
+
"@gooddata/sdk-backend-base": "11.53.0",
|
|
67
|
+
"@gooddata/sdk-embedding": "11.53.0",
|
|
68
|
+
"@gooddata/sdk-model": "11.53.0",
|
|
69
|
+
"@gooddata/sdk-backend-spi": "11.53.0",
|
|
70
|
+
"@gooddata/sdk-ui": "11.53.0",
|
|
71
|
+
"@gooddata/sdk-ui-charts": "11.53.0",
|
|
72
|
+
"@gooddata/sdk-ui-geo": "11.53.0",
|
|
73
|
+
"@gooddata/sdk-ui-filters": "11.53.0",
|
|
74
|
+
"@gooddata/sdk-ui-pivot": "11.53.0",
|
|
75
|
+
"@gooddata/sdk-ui-semantic-search": "11.53.0",
|
|
76
|
+
"@gooddata/sdk-ui-kit": "11.53.0",
|
|
77
|
+
"@gooddata/sdk-ui-theme-provider": "11.53.0",
|
|
78
|
+
"@gooddata/sdk-ui-vis-commons": "11.53.0",
|
|
79
|
+
"@gooddata/util": "11.53.0"
|
|
80
80
|
},
|
|
81
81
|
"devDependencies": {
|
|
82
82
|
"@microsoft/api-documenter": "^7.17.0",
|
|
@@ -124,12 +124,12 @@
|
|
|
124
124
|
"typescript": "5.9.3",
|
|
125
125
|
"vitest": "4.1.8",
|
|
126
126
|
"vitest-dom": "0.1.1",
|
|
127
|
-
"@gooddata/eslint-config": "11.53.0
|
|
128
|
-
"@gooddata/
|
|
129
|
-
"@gooddata/
|
|
130
|
-
"@gooddata/reference-workspace": "11.53.0
|
|
131
|
-
"@gooddata/sdk-backend-mockingbird": "11.53.0
|
|
132
|
-
"@gooddata/stylelint-config": "11.53.0
|
|
127
|
+
"@gooddata/eslint-config": "11.53.0",
|
|
128
|
+
"@gooddata/oxlint-config": "11.53.0",
|
|
129
|
+
"@gooddata/i18n-toolkit": "11.53.0",
|
|
130
|
+
"@gooddata/reference-workspace": "11.53.0",
|
|
131
|
+
"@gooddata/sdk-backend-mockingbird": "11.53.0",
|
|
132
|
+
"@gooddata/stylelint-config": "11.53.0"
|
|
133
133
|
},
|
|
134
134
|
"peerDependencies": {
|
|
135
135
|
"react": "^18.0.0 || ^19.0.0",
|
package/esm/internal/components/pluggableVisualizations/geoChartNext/geoConfigBuilder.test.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"geoConfigBuilder.test.d.ts","sourceRoot":"","sources":["../../../../../src/internal/components/pluggableVisualizations/geoChartNext/geoConfigBuilder.test.ts"],"names":[],"mappings":""}
|
package/esm/internal/components/pluggableVisualizations/geoChartNext/geoConfigBuilder.test.js
DELETED
|
@@ -1,415 +0,0 @@
|
|
|
1
|
-
// (C) 2025-2026 GoodData Corporation
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
3
|
-
import { idRef, newAttribute, newBucket, newInsightDefinition } from "@gooddata/sdk-model";
|
|
4
|
-
import { BucketNames } from "@gooddata/sdk-ui";
|
|
5
|
-
import { ANALYTICAL_ENVIRONMENT, DASHBOARDS_ENVIRONMENT } from "../../../constants/properties.js";
|
|
6
|
-
import { buildAreaVisualizationConfig } from "../geoAreaChart/geoAreaConfigBuilder.js";
|
|
7
|
-
import { buildGeoVisualizationConfig, geoConfigFromInsight, geoInsightConversion, } from "./geoConfigBuilder.js";
|
|
8
|
-
describe("geoConfigFromInsight", () => {
|
|
9
|
-
it("keeps mapStyle configuration", () => {
|
|
10
|
-
const areaAttribute = newAttribute(idRef("label.area", "displayForm"), (attribute) => attribute.localId("area_df"));
|
|
11
|
-
const insight = newInsightDefinition("local:geoArea", (builder) => builder.buckets([newBucket(BucketNames.AREA, areaAttribute)]).properties({
|
|
12
|
-
controls: {
|
|
13
|
-
mapStyle: "gd://custom-style",
|
|
14
|
-
},
|
|
15
|
-
}));
|
|
16
|
-
const config = geoConfigFromInsight(insight);
|
|
17
|
-
expect(config.mapStyle).toBe("gd://custom-style");
|
|
18
|
-
});
|
|
19
|
-
it("keeps zoom configuration", () => {
|
|
20
|
-
const areaAttribute = newAttribute(idRef("label.area", "displayForm"), (attribute) => attribute.localId("area_df"));
|
|
21
|
-
const insight = newInsightDefinition("local:geoArea", (builder) => builder.buckets([newBucket(BucketNames.AREA, areaAttribute)]).properties({
|
|
22
|
-
controls: {
|
|
23
|
-
zoom: 4,
|
|
24
|
-
},
|
|
25
|
-
}));
|
|
26
|
-
const config = geoConfigFromInsight(insight);
|
|
27
|
-
expect(config.zoom).toBe(4);
|
|
28
|
-
});
|
|
29
|
-
it("normalizes legacy legend positions from insight properties", () => {
|
|
30
|
-
const locationAttribute = newAttribute(idRef("nm_location", "displayForm"), (attribute) => attribute.localId("location_id"));
|
|
31
|
-
const insight = newInsightDefinition("local:geoPushpin", (builder) => builder.buckets([newBucket(BucketNames.LOCATION, locationAttribute)]).properties({
|
|
32
|
-
controls: {
|
|
33
|
-
legend: {
|
|
34
|
-
position: "top",
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
}));
|
|
38
|
-
const config = geoConfigFromInsight(insight);
|
|
39
|
-
expect(config.legend?.position).toBe("top-right");
|
|
40
|
-
});
|
|
41
|
-
it("maps legacy tileset visualization properties to basemap", () => {
|
|
42
|
-
const locationAttribute = newAttribute(idRef("nm_location", "displayForm"), (attribute) => attribute.localId("location_id"));
|
|
43
|
-
const insight = newInsightDefinition("local:geoPushpin", (builder) => builder.buckets([newBucket(BucketNames.LOCATION, locationAttribute)]).properties({
|
|
44
|
-
controls: {
|
|
45
|
-
tileset: "satellite",
|
|
46
|
-
viewport: {
|
|
47
|
-
area: "continent_na",
|
|
48
|
-
},
|
|
49
|
-
},
|
|
50
|
-
}));
|
|
51
|
-
const config = geoConfigFromInsight(insight);
|
|
52
|
-
expect(config.basemap).toBe("satellite");
|
|
53
|
-
expect(config.viewport?.area).toBe("continent_na");
|
|
54
|
-
});
|
|
55
|
-
it("keeps explicit basemap values unchanged", () => {
|
|
56
|
-
const locationAttribute = newAttribute(idRef("nm_location", "displayForm"), (attribute) => attribute.localId("location_id"));
|
|
57
|
-
const insight = newInsightDefinition("local:geoPushpin", (builder) => builder.buckets([newBucket(BucketNames.LOCATION, locationAttribute)]).properties({
|
|
58
|
-
controls: {
|
|
59
|
-
basemap: "monochrome",
|
|
60
|
-
tileset: "satellite",
|
|
61
|
-
},
|
|
62
|
-
}));
|
|
63
|
-
const config = geoConfigFromInsight(insight);
|
|
64
|
-
expect(config.basemap).toBe("monochrome");
|
|
65
|
-
});
|
|
66
|
-
it("keeps oneIcon points configuration", () => {
|
|
67
|
-
const locationAttribute = newAttribute(idRef("nm_location", "displayForm"), (attribute) => attribute.localId("location_id"));
|
|
68
|
-
const insight = newInsightDefinition("local:geoPushpin", (builder) => builder.buckets([newBucket(BucketNames.LOCATION, locationAttribute)]).properties({
|
|
69
|
-
controls: {
|
|
70
|
-
points: {
|
|
71
|
-
shapeType: "oneIcon",
|
|
72
|
-
icon: "airport",
|
|
73
|
-
},
|
|
74
|
-
},
|
|
75
|
-
}));
|
|
76
|
-
const config = geoConfigFromInsight(insight);
|
|
77
|
-
expect(config.points).toEqual({
|
|
78
|
-
shapeType: "oneIcon",
|
|
79
|
-
icon: "airport",
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
describe("geoInsightConversion", () => {
|
|
84
|
-
it("returns area bucket attribute when converting AREA bucket", () => {
|
|
85
|
-
const areaAttribute = newAttribute(idRef("label.area", "displayForm"), (attribute) => attribute.localId("area_df"));
|
|
86
|
-
const insight = newInsightDefinition("local:geoArea", (builder) => builder.buckets([newBucket(BucketNames.AREA, areaAttribute)]));
|
|
87
|
-
const conversion = geoInsightConversion("area", BucketNames.AREA);
|
|
88
|
-
const ctx = {};
|
|
89
|
-
const attribute = conversion.itemAccessor(insight, ctx);
|
|
90
|
-
expect(attribute?.attribute?.localIdentifier).toBe("area_df");
|
|
91
|
-
});
|
|
92
|
-
it("uses clean localId for latitude without a_ prefix", () => {
|
|
93
|
-
const locationAttribute = newAttribute(idRef("nm_location", "displayForm"), (attribute) => attribute.localId("location_id"));
|
|
94
|
-
const insight = newInsightDefinition("local:geoPushpin", (builder) => builder.buckets([newBucket(BucketNames.LOCATION, locationAttribute)]).properties({
|
|
95
|
-
controls: {
|
|
96
|
-
latitude: "nm_latitude",
|
|
97
|
-
longitude: "nm_longitude",
|
|
98
|
-
},
|
|
99
|
-
}));
|
|
100
|
-
const conversion = geoInsightConversion("latitude", BucketNames.LATITUDE);
|
|
101
|
-
const ctx = {
|
|
102
|
-
backend: { capabilities: { supportsSeparateLatitudeLongitudeLabels: true } },
|
|
103
|
-
};
|
|
104
|
-
const attribute = conversion.itemAccessor(insight, ctx);
|
|
105
|
-
expect(attribute?.attribute?.localIdentifier).toBe("latitude_df");
|
|
106
|
-
expect(attribute?.attribute?.displayForm).toEqual(idRef("nm_latitude", "displayForm"));
|
|
107
|
-
});
|
|
108
|
-
it("uses clean localId for longitude without a_ prefix", () => {
|
|
109
|
-
const locationAttribute = newAttribute(idRef("nm_location", "displayForm"), (attribute) => attribute.localId("location_id"));
|
|
110
|
-
const insight = newInsightDefinition("local:geoPushpin", (builder) => builder.buckets([newBucket(BucketNames.LOCATION, locationAttribute)]).properties({
|
|
111
|
-
controls: {
|
|
112
|
-
latitude: "nm_latitude",
|
|
113
|
-
longitude: "nm_longitude",
|
|
114
|
-
},
|
|
115
|
-
}));
|
|
116
|
-
const conversion = geoInsightConversion("longitude", BucketNames.LONGITUDE);
|
|
117
|
-
const ctx = {
|
|
118
|
-
backend: { capabilities: { supportsSeparateLatitudeLongitudeLabels: true } },
|
|
119
|
-
};
|
|
120
|
-
const attribute = conversion.itemAccessor(insight, ctx);
|
|
121
|
-
expect(attribute?.attribute?.localIdentifier).toBe("longitude_df");
|
|
122
|
-
expect(attribute?.attribute?.displayForm).toEqual(idRef("nm_longitude", "displayForm"));
|
|
123
|
-
});
|
|
124
|
-
});
|
|
125
|
-
describe("buildGeoVisualizationConfig", () => {
|
|
126
|
-
const options = {
|
|
127
|
-
messages: {},
|
|
128
|
-
};
|
|
129
|
-
it("applies viewport navigation in dashboards, leaves embedded unset, and disables in AD", () => {
|
|
130
|
-
const dashboardWidgetConfig = buildGeoVisualizationConfig({
|
|
131
|
-
options: {
|
|
132
|
-
...options,
|
|
133
|
-
config: {
|
|
134
|
-
isInEditMode: false,
|
|
135
|
-
},
|
|
136
|
-
},
|
|
137
|
-
supportedControls: {},
|
|
138
|
-
colorMapping: undefined,
|
|
139
|
-
environment: DASHBOARDS_ENVIRONMENT,
|
|
140
|
-
});
|
|
141
|
-
const embeddedConfig = buildGeoVisualizationConfig({
|
|
142
|
-
options: {
|
|
143
|
-
...options,
|
|
144
|
-
config: {},
|
|
145
|
-
},
|
|
146
|
-
supportedControls: {},
|
|
147
|
-
colorMapping: undefined,
|
|
148
|
-
environment: "",
|
|
149
|
-
});
|
|
150
|
-
const adConfig = buildGeoVisualizationConfig({
|
|
151
|
-
options: {
|
|
152
|
-
...options,
|
|
153
|
-
config: {
|
|
154
|
-
isInEditMode: true,
|
|
155
|
-
},
|
|
156
|
-
},
|
|
157
|
-
supportedControls: {},
|
|
158
|
-
colorMapping: undefined,
|
|
159
|
-
environment: ANALYTICAL_ENVIRONMENT,
|
|
160
|
-
});
|
|
161
|
-
expect(dashboardWidgetConfig.applyViewportNavigation).toBe(true);
|
|
162
|
-
expect(embeddedConfig.applyViewportNavigation).toBeUndefined();
|
|
163
|
-
expect(adConfig.applyViewportNavigation).toBe(false);
|
|
164
|
-
});
|
|
165
|
-
it("normalizes legacy legend positions and keeps new corner values", () => {
|
|
166
|
-
const config = buildGeoVisualizationConfig({
|
|
167
|
-
options,
|
|
168
|
-
supportedControls: {
|
|
169
|
-
legend: {
|
|
170
|
-
position: "bottom",
|
|
171
|
-
},
|
|
172
|
-
},
|
|
173
|
-
colorMapping: undefined,
|
|
174
|
-
environment: "",
|
|
175
|
-
});
|
|
176
|
-
const configWithCorner = buildGeoVisualizationConfig({
|
|
177
|
-
options,
|
|
178
|
-
supportedControls: {
|
|
179
|
-
legend: {
|
|
180
|
-
position: "bottom-left",
|
|
181
|
-
},
|
|
182
|
-
},
|
|
183
|
-
colorMapping: undefined,
|
|
184
|
-
environment: "",
|
|
185
|
-
});
|
|
186
|
-
expect(config.legend?.position).toBe("bottom-right");
|
|
187
|
-
expect(configWithCorner.legend?.position).toBe("bottom-left");
|
|
188
|
-
});
|
|
189
|
-
it("preserves basemap even when the broad basemap FF is disabled", () => {
|
|
190
|
-
const supportedControls = {
|
|
191
|
-
basemap: "none",
|
|
192
|
-
};
|
|
193
|
-
const disabledConfig = buildGeoVisualizationConfig({
|
|
194
|
-
options,
|
|
195
|
-
supportedControls,
|
|
196
|
-
colorMapping: undefined,
|
|
197
|
-
environment: ANALYTICAL_ENVIRONMENT,
|
|
198
|
-
});
|
|
199
|
-
const enabledConfig = buildGeoVisualizationConfig({
|
|
200
|
-
options,
|
|
201
|
-
supportedControls,
|
|
202
|
-
colorMapping: undefined,
|
|
203
|
-
environment: ANALYTICAL_ENVIRONMENT,
|
|
204
|
-
featureFlags: { enableGeoBasemapConfig: true },
|
|
205
|
-
});
|
|
206
|
-
expect(disabledConfig.enableGeoBasemapConfig).toBe(false);
|
|
207
|
-
expect(disabledConfig.basemap).toBe("none");
|
|
208
|
-
expect(enabledConfig.enableGeoBasemapConfig).toBe(true);
|
|
209
|
-
expect(enabledConfig.basemap).toBe("none");
|
|
210
|
-
});
|
|
211
|
-
it("does not let raw config or custom visualization config override sanitized supported controls", () => {
|
|
212
|
-
const rawGeoStyleConfig = {
|
|
213
|
-
basemap: "none",
|
|
214
|
-
};
|
|
215
|
-
const config = buildGeoVisualizationConfig({
|
|
216
|
-
options: {
|
|
217
|
-
...options,
|
|
218
|
-
config: {
|
|
219
|
-
isInEditMode: false,
|
|
220
|
-
...rawGeoStyleConfig,
|
|
221
|
-
},
|
|
222
|
-
customVisualizationConfig: {
|
|
223
|
-
...rawGeoStyleConfig,
|
|
224
|
-
},
|
|
225
|
-
},
|
|
226
|
-
supportedControls: {
|
|
227
|
-
basemap: "monochrome",
|
|
228
|
-
},
|
|
229
|
-
colorMapping: undefined,
|
|
230
|
-
environment: ANALYTICAL_ENVIRONMENT,
|
|
231
|
-
});
|
|
232
|
-
expect(config.basemap).toBe("monochrome");
|
|
233
|
-
});
|
|
234
|
-
it("maps legacy tileset supported controls to basemap", () => {
|
|
235
|
-
const config = buildGeoVisualizationConfig({
|
|
236
|
-
options: {
|
|
237
|
-
...options,
|
|
238
|
-
config: {
|
|
239
|
-
isInEditMode: false,
|
|
240
|
-
},
|
|
241
|
-
},
|
|
242
|
-
supportedControls: {
|
|
243
|
-
tileset: "satellite",
|
|
244
|
-
},
|
|
245
|
-
colorMapping: undefined,
|
|
246
|
-
environment: ANALYTICAL_ENVIRONMENT,
|
|
247
|
-
});
|
|
248
|
-
expect(config.basemap).toBe("satellite");
|
|
249
|
-
});
|
|
250
|
-
it("does not forward bounds when viewport config is disabled", () => {
|
|
251
|
-
const config = buildGeoVisualizationConfig({
|
|
252
|
-
options,
|
|
253
|
-
supportedControls: {
|
|
254
|
-
center: { lat: 40, lng: -74 },
|
|
255
|
-
zoom: 3,
|
|
256
|
-
bounds: {
|
|
257
|
-
southWest: { lat: 35, lng: -80 },
|
|
258
|
-
northEast: { lat: 45, lng: -68 },
|
|
259
|
-
},
|
|
260
|
-
viewport: { area: "custom" },
|
|
261
|
-
},
|
|
262
|
-
colorMapping: undefined,
|
|
263
|
-
environment: ANALYTICAL_ENVIRONMENT,
|
|
264
|
-
featureFlags: {
|
|
265
|
-
enableGeoChartsViewportConfig: false,
|
|
266
|
-
},
|
|
267
|
-
});
|
|
268
|
-
expect(config.enableGeoChartsViewportConfig).toBe(false);
|
|
269
|
-
expect(config.bounds).toBeUndefined();
|
|
270
|
-
expect(config.center).toEqual({ lat: 40, lng: -74 });
|
|
271
|
-
expect(config.zoom).toBe(3);
|
|
272
|
-
});
|
|
273
|
-
it("propagates customTooltip from supportedControls into the rendered config", () => {
|
|
274
|
-
const customTooltip = {
|
|
275
|
-
enabled: true,
|
|
276
|
-
content: "**{label/region}** — Revenue: {metric/revenue}",
|
|
277
|
-
placement: "above",
|
|
278
|
-
};
|
|
279
|
-
const config = buildGeoVisualizationConfig({
|
|
280
|
-
options,
|
|
281
|
-
supportedControls: { customTooltip },
|
|
282
|
-
colorMapping: undefined,
|
|
283
|
-
environment: DASHBOARDS_ENVIRONMENT,
|
|
284
|
-
});
|
|
285
|
-
expect(config.customTooltip).toEqual(customTooltip);
|
|
286
|
-
});
|
|
287
|
-
it("surfaces enableCustomTooltip=false from feature flags so the render gate can fire", () => {
|
|
288
|
-
const config = buildGeoVisualizationConfig({
|
|
289
|
-
options,
|
|
290
|
-
supportedControls: {},
|
|
291
|
-
colorMapping: undefined,
|
|
292
|
-
environment: DASHBOARDS_ENVIRONMENT,
|
|
293
|
-
featureFlags: { enableCustomTooltip: false },
|
|
294
|
-
});
|
|
295
|
-
expect(config.enableCustomTooltip).toBe(false);
|
|
296
|
-
});
|
|
297
|
-
});
|
|
298
|
-
describe("buildAreaVisualizationConfig", () => {
|
|
299
|
-
const options = {
|
|
300
|
-
messages: {},
|
|
301
|
-
};
|
|
302
|
-
it("applies viewport navigation in dashboards, leaves embedded unset, and disables in AD", () => {
|
|
303
|
-
const dashboardWidgetConfig = buildAreaVisualizationConfig({
|
|
304
|
-
options: {
|
|
305
|
-
...options,
|
|
306
|
-
config: {
|
|
307
|
-
isInEditMode: false,
|
|
308
|
-
},
|
|
309
|
-
},
|
|
310
|
-
supportedControls: {},
|
|
311
|
-
colorMapping: undefined,
|
|
312
|
-
environment: DASHBOARDS_ENVIRONMENT,
|
|
313
|
-
});
|
|
314
|
-
const embeddedConfig = buildAreaVisualizationConfig({
|
|
315
|
-
options: {
|
|
316
|
-
...options,
|
|
317
|
-
config: {},
|
|
318
|
-
},
|
|
319
|
-
supportedControls: {},
|
|
320
|
-
colorMapping: undefined,
|
|
321
|
-
environment: "",
|
|
322
|
-
});
|
|
323
|
-
const adConfig = buildAreaVisualizationConfig({
|
|
324
|
-
options: {
|
|
325
|
-
...options,
|
|
326
|
-
config: {
|
|
327
|
-
isInEditMode: true,
|
|
328
|
-
},
|
|
329
|
-
},
|
|
330
|
-
supportedControls: {},
|
|
331
|
-
colorMapping: undefined,
|
|
332
|
-
environment: ANALYTICAL_ENVIRONMENT,
|
|
333
|
-
});
|
|
334
|
-
expect(dashboardWidgetConfig.applyViewportNavigation).toBe(true);
|
|
335
|
-
expect(embeddedConfig.applyViewportNavigation).toBeUndefined();
|
|
336
|
-
expect(adConfig.applyViewportNavigation).toBe(false);
|
|
337
|
-
});
|
|
338
|
-
it("normalizes legacy legend positions for geo area charts", () => {
|
|
339
|
-
const config = buildAreaVisualizationConfig({
|
|
340
|
-
options,
|
|
341
|
-
supportedControls: {
|
|
342
|
-
legend: {
|
|
343
|
-
position: "left",
|
|
344
|
-
},
|
|
345
|
-
},
|
|
346
|
-
colorMapping: undefined,
|
|
347
|
-
environment: "",
|
|
348
|
-
});
|
|
349
|
-
expect(config.legend?.position).toBe("top-left");
|
|
350
|
-
});
|
|
351
|
-
it("preserves sanitized basemap config even when the broad basemap FF is disabled", () => {
|
|
352
|
-
const supportedControls = {
|
|
353
|
-
basemap: "monochrome",
|
|
354
|
-
};
|
|
355
|
-
const disabledConfig = buildAreaVisualizationConfig({
|
|
356
|
-
options,
|
|
357
|
-
supportedControls,
|
|
358
|
-
colorMapping: undefined,
|
|
359
|
-
environment: ANALYTICAL_ENVIRONMENT,
|
|
360
|
-
});
|
|
361
|
-
const enabledConfig = buildAreaVisualizationConfig({
|
|
362
|
-
options,
|
|
363
|
-
supportedControls,
|
|
364
|
-
colorMapping: undefined,
|
|
365
|
-
environment: ANALYTICAL_ENVIRONMENT,
|
|
366
|
-
featureFlags: { enableGeoBasemapConfig: true },
|
|
367
|
-
});
|
|
368
|
-
expect(disabledConfig.enableGeoBasemapConfig).toBe(false);
|
|
369
|
-
expect(disabledConfig.basemap).toBe("monochrome");
|
|
370
|
-
expect(enabledConfig.enableGeoBasemapConfig).toBe(true);
|
|
371
|
-
expect(enabledConfig.basemap).toBe("monochrome");
|
|
372
|
-
});
|
|
373
|
-
it("maps legacy tileset controls to basemap", () => {
|
|
374
|
-
const config = buildAreaVisualizationConfig({
|
|
375
|
-
options: {
|
|
376
|
-
...options,
|
|
377
|
-
config: {
|
|
378
|
-
isInEditMode: false,
|
|
379
|
-
},
|
|
380
|
-
},
|
|
381
|
-
supportedControls: {
|
|
382
|
-
controls: {
|
|
383
|
-
tileset: "satellite",
|
|
384
|
-
},
|
|
385
|
-
},
|
|
386
|
-
colorMapping: undefined,
|
|
387
|
-
environment: ANALYTICAL_ENVIRONMENT,
|
|
388
|
-
});
|
|
389
|
-
expect(config.basemap).toBe("satellite");
|
|
390
|
-
});
|
|
391
|
-
it("propagates customTooltip from supportedControls into the rendered config", () => {
|
|
392
|
-
const customTooltip = {
|
|
393
|
-
enabled: true,
|
|
394
|
-
content: "# {label/state}\n{metric/population}",
|
|
395
|
-
placement: "above",
|
|
396
|
-
};
|
|
397
|
-
const config = buildAreaVisualizationConfig({
|
|
398
|
-
options,
|
|
399
|
-
supportedControls: { customTooltip },
|
|
400
|
-
colorMapping: undefined,
|
|
401
|
-
environment: DASHBOARDS_ENVIRONMENT,
|
|
402
|
-
});
|
|
403
|
-
expect(config.customTooltip).toEqual(customTooltip);
|
|
404
|
-
});
|
|
405
|
-
it("surfaces enableCustomTooltip=false from feature flags so the render gate can fire", () => {
|
|
406
|
-
const config = buildAreaVisualizationConfig({
|
|
407
|
-
options,
|
|
408
|
-
supportedControls: {},
|
|
409
|
-
colorMapping: undefined,
|
|
410
|
-
environment: DASHBOARDS_ENVIRONMENT,
|
|
411
|
-
featureFlags: { enableCustomTooltip: false },
|
|
412
|
-
});
|
|
413
|
-
expect(config.enableCustomTooltip).toBe(false);
|
|
414
|
-
});
|
|
415
|
-
});
|