@gooddata/sdk-ui-dashboard 11.61.0-alpha.0 → 11.61.0-alpha.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.
Files changed (24) hide show
  1. package/esm/__version.d.ts +1 -1
  2. package/esm/__version.js +1 -1
  3. package/esm/model/commandHandlers/filterContext/changeFilterContextSelectionHandler.d.ts.map +1 -1
  4. package/esm/model/commandHandlers/filterContext/changeFilterContextSelectionHandler.js +13 -5
  5. package/esm/model/store/topBar/topBarSelectors.d.ts.map +1 -1
  6. package/esm/model/store/topBar/topBarSelectors.js +11 -3
  7. package/esm/model/store/unavailableObjects/copyBlockingReferences.d.ts +35 -0
  8. package/esm/model/store/unavailableObjects/copyBlockingReferences.d.ts.map +1 -0
  9. package/esm/model/store/unavailableObjects/copyBlockingReferences.js +79 -0
  10. package/esm/model/store/widgetDrills/drillRestrictionUtils.d.ts +9 -2
  11. package/esm/model/store/widgetDrills/drillRestrictionUtils.d.ts.map +1 -1
  12. package/esm/model/store/widgetDrills/drillRestrictionUtils.js +13 -6
  13. package/esm/presentation/localization/bundles/en-US.localization-bundle.js +1 -1
  14. package/esm/presentation/widget/common/configuration/FilterConfiguration.js +4 -4
  15. package/esm/presentation/widget/insight/DashboardInsight.d.ts.map +1 -1
  16. package/esm/presentation/widget/insight/DashboardInsight.js +7 -2
  17. package/esm/presentation/widget/insight/configuration/DrillTargets/DrillTargetUrlItem.d.ts.map +1 -1
  18. package/esm/presentation/widget/insight/configuration/DrillTargets/DrillTargetUrlItem.js +11 -5
  19. package/package.json +21 -21
  20. package/styles/css/main.css +19 -0
  21. package/styles/css/main.css.map +1 -1
  22. package/styles/css/sidebar.css +19 -0
  23. package/styles/css/sidebar.css.map +1 -1
  24. package/styles/scss/sidebar.scss +21 -0
@@ -1,4 +1,4 @@
1
- export declare const LIB_VERSION = "11.61.0-alpha.0";
1
+ export declare const LIB_VERSION = "11.61.0-alpha.2";
2
2
  export declare const LIB_DESCRIPTION = "GoodData SDK - Dashboard Component";
3
3
  export declare const LIB_NAME = "@gooddata/sdk-ui-dashboard";
4
4
  //# sourceMappingURL=__version.d.ts.map
package/esm/__version.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // (C) 2021 GoodData Corporation
2
2
  // DO NOT CHANGE THIS FILE, IT IS RE-GENERATED ON EVERY BUILD
3
- export const LIB_VERSION = "11.61.0-alpha.0";
3
+ export const LIB_VERSION = "11.61.0-alpha.2";
4
4
  export const LIB_DESCRIPTION = "GoodData SDK - Dashboard Component";
5
5
  export const LIB_NAME = "@gooddata/sdk-ui-dashboard";
@@ -1 +1 @@
1
- {"version":3,"file":"changeFilterContextSelectionHandler.d.ts","sourceRoot":"","sources":["../../../../src/model/commandHandlers/filterContext/changeFilterContextSelectionHandler.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAwC/C,OAAO,EAAE,KAAK,4BAA4B,EAAE,MAAM,2BAA2B,CAAC;AAmC9E,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAoGnE,wBAAiB,mCAAmC,CAChD,GAAG,EAAE,gBAAgB,EACrB,GAAG,EAAE,4BAA4B,GAClC,YAAY,CAAC,IAAI,CAAC,CA+KpB"}
1
+ {"version":3,"file":"changeFilterContextSelectionHandler.d.ts","sourceRoot":"","sources":["../../../../src/model/commandHandlers/filterContext/changeFilterContextSelectionHandler.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAwC/C,OAAO,EAAE,KAAK,4BAA4B,EAAE,MAAM,2BAA2B,CAAC;AAqC9E,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAoGnE,wBAAiB,mCAAmC,CAChD,GAAG,EAAE,gBAAgB,EACrB,GAAG,EAAE,4BAA4B,GAClC,YAAY,CAAC,IAAI,CAAC,CA2LpB"}
@@ -7,10 +7,12 @@ import { areObjRefsEqual, attributeElementsIsEmpty, dashboardAttributeFilterItem
7
7
  import { dashboardFilterToFilterContextItem } from "../../../_staging/dashboard/dashboardFilterContext.js";
8
8
  import { invalidArgumentsProvided } from "../../events/general.js";
9
9
  import { dispatchDashboardEvent } from "../../store/_infra/eventDispatcher.js";
10
+ import { isDashboardFilterRestricted } from "../../store/filtering/restrictedFilterUtils.js";
10
11
  import { selectAttributeFilterConfigsOverrides, selectAttributeFilterConfigsOverridesByTab, selectAttributeFilterConfigsSelectionTypeMap, selectAttributeFilterConfigsSelectionTypeMapByTab, } from "../../store/tabs/attributeFilterConfigs/attributeFilterConfigsSelectors.js";
11
12
  import { selectAttributeFilterDisplayFormsMap, selectFilterContextAttributeFilterByDisplayForm, selectFilterContextAttributeFilterByDisplayFormForTab, selectFilterContextAttributeFilterByLocalId, selectFilterContextAttributeFilterByLocalIdForTab, selectFilterContextAttributeFilterItemByDisplayForm, selectFilterContextAttributeFilterItemByDisplayFormForTab, selectFilterContextAttributeFilterItemByLocalId, selectFilterContextAttributeFilterItemByLocalIdForTab, selectFilterContextAttributeFilterItems, selectFilterContextAttributeFilterItemsForTab, selectFilterContextAttributeFilters, selectFilterContextAttributeFiltersForTab, selectFilterContextDateFilterByDataSet, selectFilterContextDateFilterByDataSetForTab, selectFilterContextDateFiltersWithDimension, selectFilterContextDateFiltersWithDimensionForTab, selectFilterContextMeasureValueFilters, } from "../../store/tabs/filterContext/filterContextSelectors.js";
12
13
  import { tabsActions } from "../../store/tabs/index.js";
13
14
  import { selectTabs } from "../../store/tabs/tabsSelectors.js";
15
+ import { selectUnavailableObjects } from "../../store/unavailableObjects/unavailableObjectsSelectors.js";
14
16
  import { resolveAttributeMetadata } from "../../utils/attributeResolver.js";
15
17
  import { resolveDisplayFormMetadata, } from "../../utils/displayFormResolver.js";
16
18
  import { canApplyFilterTypeToTarget } from "../dashboard/common/attributeFilterSelectionTypeCompatibility.js";
@@ -79,6 +81,9 @@ export function* changeFilterContextSelectionHandler(ctx, cmd) {
79
81
  }
80
82
  // Cross-filtering is always compatible with dashboard tabs now
81
83
  // (removed the check that prevented cross-filtering without tabs)
84
+ const unavailableObjects = yield select(selectUnavailableObjects);
85
+ // A restricted attribute filter keeps its stored selection: its label is never resolved, so the
86
+ // filter can be neither matched nor changed.
82
87
  const normalizedFilters = compact(filters.map((filter) => {
83
88
  if (isDashboardAttributeFilterItem(filter) ||
84
89
  isDashboardDateFilter(filter) ||
@@ -86,7 +91,8 @@ export function* changeFilterContextSelectionHandler(ctx, cmd) {
86
91
  return filter;
87
92
  }
88
93
  return dashboardFilterToFilterContextItem(filter, !!ctx.backend.capabilities.supportsMultipleDateFilters);
89
- }));
94
+ })).filter((filter) => !(isDashboardAttributeFilterItem(filter) &&
95
+ isDashboardFilterRestricted(filter, unavailableObjects)));
90
96
  const disabledComputedAttributeRef = yield call(findDisabledComputedAttributeRef, [
91
97
  ...normalizedFilters
92
98
  .filter(isDashboardAttributeFilterItem)
@@ -134,7 +140,7 @@ export function* changeFilterContextSelectionHandler(ctx, cmd) {
134
140
  const [[commonDateFilter], dateFiltersWithDimension] = partition(dateFilters, isDashboardCommonDateFilter);
135
141
  const selectionTypeMap = yield call(selectAttributeFilterConfigsSelectionTypeMapTabAware, tabLocalIdentifier);
136
142
  const [attributeFilterResult, commonDateFilterUpdateActions, dateFiltersUpdateActions, measureValueFilterUpdateActions,] = yield all([
137
- call(getAttributeFiltersUpdateActions, [...attributeFilters].reverse(), textAttributeFilters, attributeFilterConfigs, resetOthers, ctx, tabLocalIdentifier, selectionTypeMap, matchByLocalIdentifier),
143
+ call(getAttributeFiltersUpdateActions, [...attributeFilters].reverse(), textAttributeFilters, attributeFilterConfigs, resetOthers, unavailableObjects, ctx, tabLocalIdentifier, selectionTypeMap, matchByLocalIdentifier),
138
144
  call(getDateFilterUpdateActions, commonDateFilter, resetOthers, tabLocalIdentifier),
139
145
  call(getDateFiltersUpdateActions, dateFiltersWithDimension, resetOthers, tabLocalIdentifier),
140
146
  call(getMeasureValueFiltersUpdateActions, measureValueFilters, resetOthers),
@@ -229,7 +235,7 @@ function* findTargetFilterItemByDisplayForm(displayFormRef, tabLocalIdentifier)
229
235
  }
230
236
  return undefined;
231
237
  }
232
- function* getAttributeFiltersUpdateActions(attributeFilters, textAttributeFilters, attributeFilterConfigs, resetOthers, ctx, tabLocalIdentifier, selectionTypeMap, matchByLocalIdentifier) {
238
+ function* getAttributeFiltersUpdateActions(attributeFilters, textAttributeFilters, attributeFilterConfigs, resetOthers, unavailableObjects, ctx, tabLocalIdentifier, selectionTypeMap, matchByLocalIdentifier) {
233
239
  const updateActions = [];
234
240
  const displayFormsToResolve = [];
235
241
  const handledLocalIds = new Set();
@@ -516,7 +522,8 @@ function* getAttributeFiltersUpdateActions(attributeFilters, textAttributeFilter
516
522
  if (resetOthers) {
517
523
  const currentAttributeFilters = yield call(selectFilterContextAttributeFiltersTabAware, tabLocalIdentifier);
518
524
  // for element-based filters that have not been handled, create clear selection actions
519
- const unhandledFilters = currentAttributeFilters.filter((filter) => !handledLocalIds.has(dashboardAttributeFilterItemLocalIdentifier(filter)));
525
+ const unhandledFilters = currentAttributeFilters.filter((filter) => !handledLocalIds.has(dashboardAttributeFilterItemLocalIdentifier(filter)) &&
526
+ !isDashboardFilterRestricted(filter, unavailableObjects));
520
527
  if (unhandledFilters.length > 0) {
521
528
  updateActions.push(tabsActions.clearAttributeFiltersSelection({
522
529
  filterLocalIds: unhandledFilters.map((filter) => dashboardAttributeFilterItemLocalIdentifier(filter)),
@@ -527,7 +534,8 @@ function* getAttributeFiltersUpdateActions(attributeFilters, textAttributeFilter
527
534
  // (negative arbitrary filter with empty selection)
528
535
  const currentAllFilterItems = yield call(selectFilterContextAttributeFilterItemsTabAware, tabLocalIdentifier);
529
536
  const unhandledTextFilters = currentAllFilterItems.filter((filter) => isDashboardTextAttributeFilter(filter) &&
530
- !handledLocalIds.has(dashboardAttributeFilterItemLocalIdentifier(filter)));
537
+ !handledLocalIds.has(dashboardAttributeFilterItemLocalIdentifier(filter)) &&
538
+ !isDashboardFilterRestricted(filter, unavailableObjects));
531
539
  for (const textFilter of unhandledTextFilters) {
532
540
  const localId = dashboardAttributeFilterItemLocalIdentifier(textFilter);
533
541
  const resetFilter = {
@@ -1 +1 @@
1
- {"version":3,"file":"topBarSelectors.d.ts","sourceRoot":"","sources":["../../../../src/model/store/topBar/topBarSelectors.ts"],"names":[],"mappings":"AAoCA,OAAO,EAAE,KAAK,iBAAiB,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAG1E;;GAEG;AACH,eAAO,MAAM,sBAAsB,EAAE,iBAAiB,CAAC,OAAO,CAO7D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iCAAiC,EAAE,iBAAiB,CAAC,OAAO,CAIxE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,wBAAwB,EAAE,iBAAiB,CAAC,OAAO,CAI/D,CAAC;AAEF;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,cAAc,WAE3D;AAED;;GAEG;AACH,eAAO,MAAM,qCAAqC,EAAE,iBAAiB,CAAC,OAAO,CAK5E,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,uCAAuC,EAAE,iBAAiB,CAAC,OAAO,CAM9E,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,iBAAiB,CAAC,OAAO,CAMhE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,yBAAyB,EAAE,iBAAiB,CAAC,OAAO,CAOhE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iCAAiC,EAAE,iBAAiB,CAAC,OAAO,CAIxE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,0BAA0B,EAAE,iBAAiB,CAAC,OAAO,CAuBjE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,8BAA8B,EAAE,iBAAiB,CAAC,OAAO,CAuBrE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,EAAE,iBAAiB,CAAC,OAAO,CAMlE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,EAAE,iBAAiB,CAAC,OAAO,CAMrE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,4BAA4B,EAAE,iBAAiB,CAAC,OAAO,CAYnE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,iBAAiB,CAAC,OAAO,CAM7D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,iBAAiB,CAAC,OAAO,CAkB1D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,EAAE,iBAAiB,CAAC,OAAO,CAM5D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,iBAAiB,CAAC,OAAO,CAM1D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,EAAE,iBAAiB,CAAC,OAAO,CAK/D,CAAC"}
1
+ {"version":3,"file":"topBarSelectors.d.ts","sourceRoot":"","sources":["../../../../src/model/store/topBar/topBarSelectors.ts"],"names":[],"mappings":"AAoCA,OAAO,EAAE,KAAK,iBAAiB,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAI1E;;GAEG;AACH,eAAO,MAAM,sBAAsB,EAAE,iBAAiB,CAAC,OAAO,CAO7D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iCAAiC,EAAE,iBAAiB,CAAC,OAAO,CAIxE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,wBAAwB,EAAE,iBAAiB,CAAC,OAAO,CAI/D,CAAC;AAEF;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,cAAc,WAE3D;AAED;;GAEG;AACH,eAAO,MAAM,qCAAqC,EAAE,iBAAiB,CAAC,OAAO,CAK5E,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,uCAAuC,EAAE,iBAAiB,CAAC,OAAO,CAM9E,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,iBAAiB,CAAC,OAAO,CAMhE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,yBAAyB,EAAE,iBAAiB,CAAC,OAAO,CAOhE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iCAAiC,EAAE,iBAAiB,CAAC,OAAO,CAIxE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,0BAA0B,EAAE,iBAAiB,CAAC,OAAO,CAuBjE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,8BAA8B,EAAE,iBAAiB,CAAC,OAAO,CA8BrE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,EAAE,iBAAiB,CAAC,OAAO,CAMlE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,EAAE,iBAAiB,CAAC,OAAO,CAMrE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,4BAA4B,EAAE,iBAAiB,CAAC,OAAO,CAYnE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,iBAAiB,CAAC,OAAO,CAM7D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,iBAAiB,CAAC,OAAO,CAqB1D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,EAAE,iBAAiB,CAAC,OAAO,CAM5D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,iBAAiB,CAAC,OAAO,CAM1D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,EAAE,iBAAiB,CAAC,OAAO,CAK/D,CAAC"}
@@ -10,6 +10,7 @@ import { selectCanCreateAnalyticalDashboard, selectCanExportPdf, } from "../perm
10
10
  import { selectIsInEditMode, selectIsInViewMode } from "../renderMode/renderModeSelectors.js";
11
11
  import { selectIsLayoutEmpty } from "../tabs/layout/layoutSelectors.js";
12
12
  import { selectMenuButtonItemsVisibility } from "../ui/uiSelectors.js";
13
+ import { selectHasCopyBlockingReferences } from "../unavailableObjects/copyBlockingReferences.js";
13
14
  /**
14
15
  * @internal
15
16
  */
@@ -65,7 +66,7 @@ export const selectIsShareButtonVisible = createSelector(selectHasSharePermissio
65
66
  /**
66
67
  * @internal
67
68
  */
68
- export const selectIsSaveAsNewButtonVisible = createSelector(selectIsSaveAsNewButtonHidden, selectCanEnterEditModeAndIsLoaded, selectCanCreateAnalyticalDashboard, selectIsExport, selectIsReadOnly, (isSaveAsButtonHidden, isDashboardEditable, canCreateDashboard, isExport, isReadOnly) => {
69
+ export const selectIsSaveAsNewButtonVisible = createSelector(selectIsSaveAsNewButtonHidden, selectCanEnterEditModeAndIsLoaded, selectCanCreateAnalyticalDashboard, selectIsExport, selectIsReadOnly, selectHasCopyBlockingReferences, (isSaveAsButtonHidden, isDashboardEditable, canCreateDashboard, isExport, isReadOnly, isCopyBlocked) => {
69
70
  /*
70
71
  * The reasoning behind this condition is as follows. Do not show separate Save As button if:
71
72
  *
@@ -77,8 +78,14 @@ export const selectIsSaveAsNewButtonVisible = createSelector(selectIsSaveAsNewBu
77
78
  * 4. dashboard is not in export mode
78
79
  * 5. If the user cannot create dashboards - e.g. does not have permissions to do so (is viewer for example).
79
80
  * 6. If the dashboard is in read-only mode.
81
+ * 7. If the backend would refuse the copy because the dashboard references objects the user may not read.
80
82
  */
81
- return (!isSaveAsButtonHidden && !isDashboardEditable && !isExport && canCreateDashboard && !isReadOnly);
83
+ return (!isSaveAsButtonHidden &&
84
+ !isDashboardEditable &&
85
+ !isExport &&
86
+ canCreateDashboard &&
87
+ !isReadOnly &&
88
+ !isCopyBlocked);
82
89
  });
83
90
  /**
84
91
  * Selects whether the common export is available.
@@ -121,10 +128,11 @@ export const selectPdfExportVisible = createSelector(selectCommonExportAvailable
121
128
  *
122
129
  * @internal
123
130
  */
124
- export const selectSaveAsVisible = createSelector(selectIsSaveAsNewButtonVisible, selectCanCreateAnalyticalDashboard, selectIsSaveAsNewButtonHidden, selectMenuButtonItemsVisibility, (isStandaloneSaveAsNewButtonVisible, canCreateDashboard, isSaveAsNewHidden, menuButtonItemsVisibility) => {
131
+ export const selectSaveAsVisible = createSelector(selectIsSaveAsNewButtonVisible, selectCanCreateAnalyticalDashboard, selectIsSaveAsNewButtonHidden, selectMenuButtonItemsVisibility, selectHasCopyBlockingReferences, (isStandaloneSaveAsNewButtonVisible, canCreateDashboard, isSaveAsNewHidden, menuButtonItemsVisibility, isCopyBlocked) => {
125
132
  return (!isStandaloneSaveAsNewButtonVisible &&
126
133
  canCreateDashboard &&
127
134
  !isSaveAsNewHidden &&
135
+ !isCopyBlocked &&
128
136
  (menuButtonItemsVisibility.saveAsNewButton ?? true));
129
137
  });
130
138
  /**
@@ -0,0 +1,35 @@
1
+ import type { IUnavailableDashboardReference } from "@gooddata/sdk-backend-spi";
2
+ import { type FilterContextItem, type IDashboardAttributeFilterConfig, type IDashboardLayout, type IInsight } from "@gooddata/sdk-model";
3
+ import { type ExtendedDashboardWidget } from "../../types/layoutTypes.js";
4
+ import { type DashboardSelector } from "../types.js";
5
+ /**
6
+ * The parts of one dashboard tab a copy of the dashboard is created from.
7
+ *
8
+ * @internal
9
+ */
10
+ export interface IDashboardCopySourceTab {
11
+ layout?: IDashboardLayout<ExtendedDashboardWidget>;
12
+ filters: FilterContextItem[];
13
+ attributeFilterConfigs: IDashboardAttributeFilterConfig[];
14
+ }
15
+ /**
16
+ * Tells whether a copy of the dashboard would reference an object the current user may not read in a
17
+ * place the backend checks when it creates the copy, so it would refuse the copy.
18
+ *
19
+ * @remarks
20
+ * The backend accepts a copy that keeps a drill to a dashboard the user cannot open, an ignored
21
+ * drill-down hierarchy, or a metric the user may not read in a measure value filter or in the
22
+ * limit of a filter's values, so none of these count. A merely deleted object does not count either.
23
+ *
24
+ * @internal
25
+ */
26
+ export declare function hasCopyBlockingReference(tabs: IDashboardCopySourceTab[], insights: IInsight[], unavailableObjects: IUnavailableDashboardReference[]): boolean;
27
+ /**
28
+ * Selects whether "Save as new" would fail because the dashboard, as it stands now, references objects
29
+ * the current user may not read in places the backend refuses to copy. Removing those references in
30
+ * edit mode clears it. The copy is made from every tab, and in view mode from the saved filters.
31
+ *
32
+ * @internal
33
+ */
34
+ export declare const selectHasCopyBlockingReferences: DashboardSelector<boolean>;
35
+ //# sourceMappingURL=copyBlockingReferences.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copyBlockingReferences.d.ts","sourceRoot":"","sources":["../../../../src/model/store/unavailableObjects/copyBlockingReferences.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,2BAA2B,CAAC;AAChF,OAAO,EACH,KAAK,iBAAiB,EAEtB,KAAK,+BAA+B,EACpC,KAAK,gBAAgB,EAErB,KAAK,QAAQ,EAehB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,KAAK,uBAAuB,EAAmC,MAAM,4BAA4B,CAAC;AAK3G,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAKrD;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACpC,MAAM,CAAC,EAAE,gBAAgB,CAAC,uBAAuB,CAAC,CAAC;IACnD,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,sBAAsB,EAAE,+BAA+B,EAAE,CAAC;CAC7D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CACpC,IAAI,EAAE,uBAAuB,EAAE,EAC/B,QAAQ,EAAE,QAAQ,EAAE,EACpB,kBAAkB,EAAE,8BAA8B,EAAE,GACrD,OAAO,CAkET;AAED;;;;;;GAMG;AACH,eAAO,MAAM,+BAA+B,EAAE,iBAAiB,CAAC,OAAO,CAqBtE,CAAC"}
@@ -0,0 +1,79 @@
1
+ // (C) 2026 GoodData Corporation
2
+ import { createSelector } from "@reduxjs/toolkit";
3
+ import { areObjRefsEqual, dashboardAttributeFilterItemDisplayForm, insightRef, isComputedAttributeRef, isDashboardAttributeFilterItem, isDashboardAttributeFilterReference, isDrillToDashboard, isInsightWidget, isKpiWidget, isRichTextWidget, isVisualizationSwitcherWidget, } from "@gooddata/sdk-model";
4
+ import { collectReferences } from "@gooddata/sdk-ui-kit";
5
+ import { isExtendedDashboardLayoutWidget } from "../../types/layoutTypes.js";
6
+ import { isDashboardObjectRestricted } from "../filtering/restrictedFilterUtils.js";
7
+ import { selectInsights } from "../insights/insightsSelectors.js";
8
+ import { selectIsInViewMode } from "../renderMode/renderModeSelectors.js";
9
+ import { selectTabs } from "../tabs/tabsSelectors.js";
10
+ import { isDrillRestricted } from "../widgetDrills/drillRestrictionUtils.js";
11
+ import { selectUnavailableObjects } from "./unavailableObjectsSelectors.js";
12
+ /**
13
+ * Tells whether a copy of the dashboard would reference an object the current user may not read in a
14
+ * place the backend checks when it creates the copy, so it would refuse the copy.
15
+ *
16
+ * @remarks
17
+ * The backend accepts a copy that keeps a drill to a dashboard the user cannot open, an ignored
18
+ * drill-down hierarchy, or a metric the user may not read in a measure value filter or in the
19
+ * limit of a filter's values, so none of these count. A merely deleted object does not count either.
20
+ *
21
+ * @internal
22
+ */
23
+ export function hasCopyBlockingReference(tabs, insights, unavailableObjects) {
24
+ const isForbidden = (ref, type) => ref !== undefined && isDashboardObjectRestricted(ref, type, unavailableObjects);
25
+ const isLabelForbidden = (ref) => ref !== undefined &&
26
+ isForbidden(ref, isComputedAttributeRef(ref) ? "computedAttribute" : "displayForm");
27
+ const isTextForbidden = (text) => Object.values(collectReferences(text ?? "")).some(({ ref, type }) => isForbidden(ref, type));
28
+ const isAnalyticalWidgetForbidden = (widget) => isTextForbidden(widget.description) ||
29
+ widget.ignoreDashboardFilters.some((reference) => isDashboardAttributeFilterReference(reference) && isLabelForbidden(reference.displayForm));
30
+ const isInsightWidgetForbidden = (widget) => {
31
+ // Save as new recomputes custom URL references from the insight as it is now, so resolve them the same way
32
+ const insight = insights.find((candidate) => areObjRefsEqual(insightRef(candidate), widget.insight));
33
+ return (isAnalyticalWidgetForbidden(widget) ||
34
+ isForbidden(widget.insight, "insight") ||
35
+ widget.drills.some((drill) => !isDrillToDashboard(drill) && isDrillRestricted(drill, unavailableObjects, insight)));
36
+ };
37
+ const isWidgetForbidden = (widget) => {
38
+ if (isExtendedDashboardLayoutWidget(widget)) {
39
+ return widget.sections.some(isSectionForbidden);
40
+ }
41
+ if (isInsightWidget(widget)) {
42
+ return isInsightWidgetForbidden(widget);
43
+ }
44
+ if (isVisualizationSwitcherWidget(widget)) {
45
+ return (isAnalyticalWidgetForbidden(widget) || widget.visualizations.some(isInsightWidgetForbidden));
46
+ }
47
+ if (isKpiWidget(widget)) {
48
+ return isAnalyticalWidgetForbidden(widget) || isForbidden(widget.kpi.metric, "measure");
49
+ }
50
+ if (isRichTextWidget(widget)) {
51
+ return isAnalyticalWidgetForbidden(widget) || isTextForbidden(widget.content);
52
+ }
53
+ return false;
54
+ };
55
+ const isSectionForbidden = (section) => isTextForbidden(section.header?.description) ||
56
+ section.items.some((item) => item.widget !== undefined && isWidgetForbidden(item.widget));
57
+ return tabs.some((tab) => (tab.layout?.sections ?? []).some(isSectionForbidden) ||
58
+ tab.attributeFilterConfigs.some((config) => isLabelForbidden(config.displayAsLabel)) ||
59
+ tab.filters.some((filter) => isDashboardAttributeFilterItem(filter) &&
60
+ isLabelForbidden(dashboardAttributeFilterItemDisplayForm(filter))));
61
+ }
62
+ /**
63
+ * Selects whether "Save as new" would fail because the dashboard, as it stands now, references objects
64
+ * the current user may not read in places the backend refuses to copy. Removing those references in
65
+ * edit mode clears it. The copy is made from every tab, and in view mode from the saved filters.
66
+ *
67
+ * @internal
68
+ */
69
+ export const selectHasCopyBlockingReferences = createSelector(selectTabs, selectInsights, selectIsInViewMode, selectUnavailableObjects, (tabs, insights, isInViewMode, unavailableObjects) => unavailableObjects.length > 0 &&
70
+ hasCopyBlockingReference((tabs ?? []).map((tab) => {
71
+ const filterContext = isInViewMode
72
+ ? tab.filterContext?.originalFilterContextDefinition
73
+ : tab.filterContext?.filterContextDefinition;
74
+ return {
75
+ layout: tab.layout?.layout,
76
+ filters: filterContext?.filters ?? [],
77
+ attributeFilterConfigs: tab.attributeFilterConfigs?.attributeFilterConfigs ?? [],
78
+ };
79
+ }), insights, unavailableObjects));
@@ -1,5 +1,12 @@
1
1
  import { type IUnavailableDashboardReference } from "@gooddata/sdk-backend-spi";
2
+ import { type IInsightDefinition, type ObjRef } from "@gooddata/sdk-model";
2
3
  import { type DashboardDrillDefinition } from "../../../types.js";
3
- /** Identifies positively reported restrictions; missing metadata alone is not a permission signal. */
4
- export declare function isDrillRestricted(drill: DashboardDrillDefinition, unavailable: IUnavailableDashboardReference[]): boolean;
4
+ export declare function isLabelRestricted(ref: ObjRef, unavailable: IUnavailableDashboardReference[]): boolean;
5
+ /**
6
+ * Identifies positively reported restrictions; missing metadata alone is not a permission signal.
7
+ *
8
+ * @param insight - the drilled widget's insight; custom URL placeholders that depend on it are then
9
+ * resolved from the insight as it is now rather than from the references stored on the drill
10
+ */
11
+ export declare function isDrillRestricted(drill: DashboardDrillDefinition, unavailable: IUnavailableDashboardReference[], insight?: IInsightDefinition): boolean;
5
12
  //# sourceMappingURL=drillRestrictionUtils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"drillRestrictionUtils.d.ts","sourceRoot":"","sources":["../../../../src/model/store/widgetDrills/drillRestrictionUtils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,8BAA8B,EAAE,MAAM,2BAA2B,CAAC;AAYhF,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAGlE,sGAAsG;AACtG,wBAAgB,iBAAiB,CAC7B,KAAK,EAAE,wBAAwB,EAC/B,WAAW,EAAE,8BAA8B,EAAE,GAC9C,OAAO,CAiCT"}
1
+ {"version":3,"file":"drillRestrictionUtils.d.ts","sourceRoot":"","sources":["../../../../src/model/store/widgetDrills/drillRestrictionUtils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,8BAA8B,EAAE,MAAM,2BAA2B,CAAC;AAChF,OAAO,EACH,KAAK,kBAAkB,EACvB,KAAK,MAAM,EAOd,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAGlE,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,8BAA8B,EAAE,GAAG,OAAO,CAMrG;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC7B,KAAK,EAAE,wBAAwB,EAC/B,WAAW,EAAE,8BAA8B,EAAE,EAC7C,OAAO,CAAC,EAAE,kBAAkB,GAC7B,OAAO,CA2BT"}
@@ -2,8 +2,16 @@
2
2
  import { isComputedAttributeRef, isDrillToAttributeUrl, isDrillToCustomUrl, isDrillToDashboard, isDrillToInsight, isIdentifierRef, } from "@gooddata/sdk-model";
3
3
  import { getDrillToCustomUrlReferences } from "@gooddata/sdk-model/internal";
4
4
  import { isDashboardObjectRestricted } from "../filtering/restrictedFilterUtils.js";
5
- /** Identifies positively reported restrictions; missing metadata alone is not a permission signal. */
6
- export function isDrillRestricted(drill, unavailable) {
5
+ export function isLabelRestricted(ref, unavailable) {
6
+ return isDashboardObjectRestricted(ref, isComputedAttributeRef(ref) ? "computedAttribute" : "displayForm", unavailable);
7
+ }
8
+ /**
9
+ * Identifies positively reported restrictions; missing metadata alone is not a permission signal.
10
+ *
11
+ * @param insight - the drilled widget's insight; custom URL placeholders that depend on it are then
12
+ * resolved from the insight as it is now rather than from the references stored on the drill
13
+ */
14
+ export function isDrillRestricted(drill, unavailable, insight) {
7
15
  if (unavailable.length === 0) {
8
16
  return false;
9
17
  }
@@ -13,13 +21,12 @@ export function isDrillRestricted(drill, unavailable) {
13
21
  if (isDrillToDashboard(drill)) {
14
22
  return (!!drill.target && isDashboardObjectRestricted(drill.target, "analyticalDashboard", unavailable));
15
23
  }
16
- const isLabelRestricted = (ref) => isDashboardObjectRestricted(ref, isComputedAttributeRef(ref) ? "computedAttribute" : "displayForm", unavailable);
17
24
  if (isDrillToAttributeUrl(drill)) {
18
- return (isLabelRestricted(drill.target.displayForm) ||
19
- isLabelRestricted(drill.target.hyperlinkDisplayForm));
25
+ return (isLabelRestricted(drill.target.displayForm, unavailable) ||
26
+ isLabelRestricted(drill.target.hyperlinkDisplayForm, unavailable));
20
27
  }
21
28
  if (isDrillToCustomUrl(drill)) {
22
- return getDrillToCustomUrlReferences(drill.target).some((ref) => isIdentifierRef(ref) &&
29
+ return getDrillToCustomUrlReferences(drill.target, insight).some((ref) => isIdentifierRef(ref) &&
23
30
  ref.type !== undefined &&
24
31
  isDashboardObjectRestricted(ref, ref.type, unavailable));
25
32
  }
@@ -2627,7 +2627,7 @@ export const en_US = {
2627
2627
  },
2628
2628
  "drill_modal_picker.restricted": {
2629
2629
  "text": "Restricted",
2630
- "crowdinContext": "Disabled drill menu target the user cannot access; replaces its title and shows a lock icon"
2630
+ "crowdinContext": "Replaces the title of a drill target the user cannot access: a disabled drill menu item with a lock icon, or the drill to URL target button in the widget's Interactions settings"
2631
2631
  },
2632
2632
  "visualizationSwitcher.restrictedEntry": {
2633
2633
  "text": "Restricted",
@@ -7,16 +7,16 @@ import { useAttributeFilterDisplayFormFromMap } from "../../../../_staging/share
7
7
  import { useAttributes } from "../../../../_staging/sharedHooks/useAttributes.js";
8
8
  import { useDashboardSelector } from "../../../../model/react/DashboardStoreProvider.js";
9
9
  import { selectAllCatalogAttributesMap, selectAllCatalogDateDatasetsMap, selectAllCatalogMeasuresMap, selectCatalogIsLoaded, } from "../../../../model/store/catalog/catalogSelectors.js";
10
+ import { selectExecutableDashboardFilters } from "../../../../model/store/filtering/dashboardFilterSelectors.js";
10
11
  import { selectInsightByRef } from "../../../../model/store/insights/insightsSelectors.js";
11
12
  import { selectAttributeFilterConfigsDisplayAsLabelMap } from "../../../../model/store/tabs/attributeFilterConfigs/attributeFilterConfigsSelectors.js";
12
- import { selectFilterContextFilters } from "../../../../model/store/tabs/filterContext/filterContextSelectors.js";
13
13
  import { AttributeFilterConfigurationItem } from "./AttributeFilterConfigurationItem.js";
14
14
  import { DateFilterConfigurationItem } from "./DateFilterConfigurationItem.js";
15
15
  import { MeasureValueFilterConfigurationItem } from "./MeasureValueFilterConfigurationItem.js";
16
16
  import { useMeasureValueFilterCompatibility } from "./useMeasureValueFilterCompatibility.js";
17
17
  import { getAttributeByDisplayForm } from "./utils.js";
18
18
  export function FilterConfiguration({ widget }) {
19
- const allFilters = useDashboardSelector(selectFilterContextFilters);
19
+ const filters = useDashboardSelector(selectExecutableDashboardFilters);
20
20
  const getAttributeFilterDisplayFormFromMap = useAttributeFilterDisplayFormFromMap();
21
21
  const attrMap = useDashboardSelector(selectAllCatalogAttributesMap);
22
22
  const ddsMap = useDashboardSelector(selectAllCatalogDateDatasetsMap);
@@ -24,10 +24,10 @@ export function FilterConfiguration({ widget }) {
24
24
  const isCatalogLoaded = useDashboardSelector(selectCatalogIsLoaded);
25
25
  const displayAsLabelMap = useDashboardSelector(selectAttributeFilterConfigsDisplayAsLabelMap);
26
26
  const draggableFilters = useMemo(() => {
27
- return allFilters.filter((f) => isDashboardAttributeFilterItem(f) ||
27
+ return filters.filter((f) => isDashboardAttributeFilterItem(f) ||
28
28
  isDashboardDateFilterWithDimension(f) ||
29
29
  isDashboardMeasureValueFilter(f));
30
- }, [allFilters]);
30
+ }, [filters]);
31
31
  const attributeFilters = useMemo(() => {
32
32
  return draggableFilters.filter(isDashboardAttributeFilterItem);
33
33
  }, [draggableFilters]);
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardInsight.d.ts","sourceRoot":"","sources":["../../../../src/presentation/widget/insight/DashboardInsight.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,YAAY,EAAW,MAAM,OAAO,CAAC;AAInD,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAIpD,OAAO,EAAE,KAAK,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEzD;;GAEG;AACH,wBAAgB,gBAAgB,CAC5B,KAAK,EAAE,IAAI,CAAC,sBAAsB,EAAE,SAAS,CAAC,GAAG;IAAE,OAAO,CAAC,EAAE,QAAQ,CAAA;CAAE,GACxE,YAAY,CAqBd"}
1
+ {"version":3,"file":"DashboardInsight.d.ts","sourceRoot":"","sources":["../../../../src/presentation/widget/insight/DashboardInsight.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,YAAY,EAAW,MAAM,OAAO,CAAC;AAInD,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAKpD,OAAO,EAAE,KAAK,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEzD;;GAEG;AACH,wBAAgB,gBAAgB,CAC5B,KAAK,EAAE,IAAI,CAAC,sBAAsB,EAAE,SAAS,CAAC,GAAG;IAAE,OAAO,CAAC,EAAE,QAAQ,CAAA;CAAE,GACxE,YAAY,CAiCd"}
@@ -3,16 +3,21 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { useMemo } from "react";
4
4
  import { useIntl } from "react-intl";
5
5
  import { useDashboardComponentsContext } from "../../dashboardContexts/DashboardComponentsContext.js";
6
+ import { useVisualizationExportData } from "../../export/useExportData.js";
6
7
  /**
7
8
  * @internal
8
9
  */
9
10
  export function DashboardInsight(props) {
10
- const { insight, widget } = props;
11
+ const { insight, widget, exportData } = props;
11
12
  const intl = useIntl();
12
13
  const { InsightWidgetComponentSet, ErrorComponent } = useDashboardComponentsContext();
13
14
  const InsightComponent = useMemo(() => (insight ? InsightWidgetComponentSet.MainComponentProvider(insight, widget) : null), [InsightWidgetComponentSet, insight, widget]);
15
+ const exportDataVis = useVisualizationExportData(exportData, false, true);
14
16
  if (!insight || !InsightComponent) {
15
- return (_jsx(ErrorComponent, { code: "404", message: intl.formatMessage({ id: "widget.error.missing_insight.message" }), description: intl.formatMessage({ id: "widget.error.missing_insight.description" }) }));
17
+ const error = (_jsx(ErrorComponent, { code: "404", message: intl.formatMessage({ id: "widget.error.missing_insight.message" }), description: intl.formatMessage({ id: "widget.error.missing_insight.description" }) }));
18
+ // slides export waits for every widget's content element and fails the whole export after
19
+ // its 180 s timeout when one is missing
20
+ return exportData && exportDataVis ? (_jsx("div", { className: "visualization-content", ...exportDataVis, children: error })) : (error);
16
21
  }
17
22
  return _jsx(InsightComponent, { ...props, insight: insight });
18
23
  }
@@ -1 +1 @@
1
- {"version":3,"file":"DrillTargetUrlItem.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/widget/insight/configuration/DrillTargets/DrillTargetUrlItem.tsx"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,oBAAoB,EAAE,KAAK,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAc7E,OAAO,EACH,KAAK,cAAc,EAGtB,MAAM,4BAA4B,CAAC;AA6BpC,MAAM,WAAW,kBAAkB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,UAAU,EAAE,oBAAoB,EAAE,CAAC;IACnC,QAAQ,EAAE,CAAC,cAAc,EAAE,cAAc,KAAK,IAAI,CAAC;CACtD;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,kBAAkB,2CAiG3D"}
1
+ {"version":3,"file":"DrillTargetUrlItem.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/widget/insight/configuration/DrillTargets/DrillTargetUrlItem.tsx"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,oBAAoB,EAAE,KAAK,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAgB7E,OAAO,EACH,KAAK,cAAc,EAGtB,MAAM,4BAA4B,CAAC;AAiCpC,MAAM,WAAW,kBAAkB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,UAAU,EAAE,oBAAoB,EAAE,CAAC;IACnC,QAAQ,EAAE,CAAC,cAAc,EAAE,cAAc,KAAK,IAAI,CAAC;CACtD;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,kBAAkB,2CAiG3D"}
@@ -2,13 +2,14 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
2
2
  // (C) 2020-2026 GoodData Corporation
3
3
  import { useState } from "react";
4
4
  import { useIntl } from "react-intl";
5
- import { invariant } from "ts-invariant";
6
5
  import { useClientWorkspaceIdentifiers } from "@gooddata/sdk-ui";
7
6
  import { Button, Dropdown } from "@gooddata/sdk-ui-kit";
8
7
  import { useDashboardSelector } from "../../../../../model/react/DashboardStoreProvider.js";
9
8
  import { selectBackendCapabilities } from "../../../../../model/store/backendCapabilities/backendCapabilitiesSelectors.js";
10
9
  import { selectAllCatalogAttributesMap, selectAllCatalogDisplayFormsMap, } from "../../../../../model/store/catalog/catalogSelectors.js";
11
10
  import { selectSettings } from "../../../../../model/store/config/configSelectors.js";
11
+ import { selectUnavailableObjects } from "../../../../../model/store/unavailableObjects/unavailableObjectsSelectors.js";
12
+ import { isLabelRestricted } from "../../../../../model/store/widgetDrills/drillRestrictionUtils.js";
12
13
  import { AttributeUrlSection } from "../../../../drill/DrillConfigPanel/DrillToUrl/AttributeUrlSection.js";
13
14
  import { CustomUrlEditor } from "../../../../drill/DrillConfigPanel/DrillToUrl/CustomUrlEditor.js";
14
15
  import { CustomUrlSection } from "../../../../drill/DrillConfigPanel/DrillToUrl/CustomUrlSection.js";
@@ -19,15 +20,20 @@ function useButtonValue(urlDrillTarget) {
19
20
  const intl = useIntl();
20
21
  const displayForms = useDashboardSelector(selectAllCatalogDisplayFormsMap);
21
22
  const attributes = useDashboardSelector(selectAllCatalogAttributesMap);
23
+ const unavailableObjects = useDashboardSelector(selectUnavailableObjects);
22
24
  if (isDrillToCustomUrlConfig(urlDrillTarget) && urlDrillTarget.customUrl) {
23
25
  return urlDrillTarget.customUrl;
24
26
  }
25
27
  if (isDrillToAttributeUrlConfig(urlDrillTarget) && urlDrillTarget.drillToAttributeDisplayForm) {
26
28
  const displayForm = displayForms.get(urlDrillTarget.drillToAttributeDisplayForm);
27
- invariant(displayForm, "inconsistent state in drill to URL button");
28
- const attribute = attributes.get(displayForm.attribute);
29
- invariant(attribute, "inconsistent state in drill to URL button");
30
- return `${attribute.attribute.title} (${displayForm.title})`;
29
+ const attribute = displayForm && attributes.get(displayForm.attribute);
30
+ if (displayForm && attribute) {
31
+ return `${attribute.attribute.title} (${displayForm.title})`;
32
+ }
33
+ // restricted or deleted labels are not in the catalog
34
+ if (isLabelRestricted(urlDrillTarget.drillToAttributeDisplayForm, unavailableObjects)) {
35
+ return intl.formatMessage({ id: "drill_modal_picker.restricted" });
36
+ }
31
37
  }
32
38
  return intl.formatMessage({ id: "configurationPanel.drillIntoUrl.defaultButtonValue" });
33
39
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gooddata/sdk-ui-dashboard",
3
- "version": "11.61.0-alpha.0",
3
+ "version": "11.61.0-alpha.2",
4
4
  "description": "GoodData SDK - Dashboard Component",
5
5
  "license": "LicenseRef-LICENSE",
6
6
  "author": "GoodData Corporation",
@@ -60,20 +60,20 @@
60
60
  "ts-invariant": "0.10.3",
61
61
  "tslib": "2.8.1",
62
62
  "uuid": "11.1.1",
63
- "@gooddata/sdk-backend-spi": "11.61.0-alpha.0",
64
- "@gooddata/sdk-backend-base": "11.61.0-alpha.0",
65
- "@gooddata/sdk-model": "11.61.0-alpha.0",
66
- "@gooddata/sdk-ui-charts": "11.61.0-alpha.0",
67
- "@gooddata/sdk-ui": "11.61.0-alpha.0",
68
- "@gooddata/sdk-ui-ext": "11.61.0-alpha.0",
69
- "@gooddata/sdk-ui-filters": "11.61.0-alpha.0",
70
- "@gooddata/sdk-ui-geo": "11.61.0-alpha.0",
71
- "@gooddata/sdk-ui-pivot": "11.61.0-alpha.0",
72
- "@gooddata/sdk-ui-kit": "11.61.0-alpha.0",
73
- "@gooddata/sdk-ui-semantic-search": "11.61.0-alpha.0",
74
- "@gooddata/sdk-ui-theme-provider": "11.61.0-alpha.0",
75
- "@gooddata/util": "11.61.0-alpha.0",
76
- "@gooddata/sdk-ui-vis-commons": "11.61.0-alpha.0"
63
+ "@gooddata/sdk-backend-base": "11.61.0-alpha.2",
64
+ "@gooddata/sdk-model": "11.61.0-alpha.2",
65
+ "@gooddata/sdk-backend-spi": "11.61.0-alpha.2",
66
+ "@gooddata/sdk-ui": "11.61.0-alpha.2",
67
+ "@gooddata/sdk-ui-charts": "11.61.0-alpha.2",
68
+ "@gooddata/sdk-ui-ext": "11.61.0-alpha.2",
69
+ "@gooddata/sdk-ui-filters": "11.61.0-alpha.2",
70
+ "@gooddata/sdk-ui-geo": "11.61.0-alpha.2",
71
+ "@gooddata/sdk-ui-pivot": "11.61.0-alpha.2",
72
+ "@gooddata/sdk-ui-kit": "11.61.0-alpha.2",
73
+ "@gooddata/sdk-ui-semantic-search": "11.61.0-alpha.2",
74
+ "@gooddata/sdk-ui-theme-provider": "11.61.0-alpha.2",
75
+ "@gooddata/sdk-ui-vis-commons": "11.61.0-alpha.2",
76
+ "@gooddata/util": "11.61.0-alpha.2"
77
77
  },
78
78
  "devDependencies": {
79
79
  "@microsoft/api-documenter": "7.30.16",
@@ -115,12 +115,12 @@
115
115
  "typescript": "7.0.2",
116
116
  "vitest": "4.1.11",
117
117
  "vitest-dom": "0.1.1",
118
- "@gooddata/i18n-toolkit": "11.61.0-alpha.0",
119
- "@gooddata/eslint-config": "11.61.0-alpha.0",
120
- "@gooddata/oxlint-config": "11.61.0-alpha.0",
121
- "@gooddata/stylelint-config": "11.61.0-alpha.0",
122
- "@gooddata/reference-workspace": "11.61.0-alpha.0",
123
- "@gooddata/sdk-backend-mockingbird": "11.61.0-alpha.0"
118
+ "@gooddata/eslint-config": "11.61.0-alpha.2",
119
+ "@gooddata/i18n-toolkit": "11.61.0-alpha.2",
120
+ "@gooddata/oxlint-config": "11.61.0-alpha.2",
121
+ "@gooddata/sdk-backend-mockingbird": "11.61.0-alpha.2",
122
+ "@gooddata/reference-workspace": "11.61.0-alpha.2",
123
+ "@gooddata/stylelint-config": "11.61.0-alpha.2"
124
124
  },
125
125
  "peerDependencies": {
126
126
  "react": "^18.0.0 || ^19.0.0",
@@ -28987,6 +28987,25 @@ figure {
28987
28987
  transition: opacity var(--sidebar-fade-duration) var(--sidebar-fade-delay);
28988
28988
  }
28989
28989
 
28990
+ .gd-sidebar-container--rail.gd-sidebar-container--collapsed .gd-dropzone-delete {
28991
+ top: 48px;
28992
+ right: auto;
28993
+ width: 96px;
28994
+ }
28995
+ .gd-sidebar-container--rail.gd-sidebar-container--collapsed .gd-dropzone-delete .gd-dropzone-message {
28996
+ display: flex;
28997
+ align-items: center;
28998
+ gap: 10px;
28999
+ writing-mode: vertical-rl;
29000
+ transform: rotate(180deg);
29001
+ font-size: 12px;
29002
+ }
29003
+ .gd-sidebar-container--rail.gd-sidebar-container--collapsed .gd-dropzone-delete .gd-dropzone-message::before {
29004
+ line-height: 1;
29005
+ font-size: 24px;
29006
+ text-orientation: sideways;
29007
+ }
29008
+
28990
29009
  .gd-sidebar-rail-search {
28991
29010
  position: absolute;
28992
29011
  top: 21px;