@gooddata/sdk-ui-dashboard 11.67.0-alpha.3 → 11.67.0-alpha.4
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/NOTICE +16 -233
- package/esm/__version.d.ts +1 -1
- package/esm/__version.js +1 -1
- package/esm/model/commandHandlers/dashboard/changeDashboardDefinitionHandler.d.ts.map +1 -1
- package/esm/model/commandHandlers/dashboard/changeDashboardDefinitionHandler.js +3 -1
- package/esm/model/commandHandlers/dashboard/common/filterViews.js +10 -3
- package/esm/model/commandHandlers/dashboard/common/overrideFiltersAcrossTabs.d.ts +46 -0
- package/esm/model/commandHandlers/dashboard/common/overrideFiltersAcrossTabs.d.ts.map +1 -0
- package/esm/model/commandHandlers/dashboard/common/overrideFiltersAcrossTabs.js +155 -0
- package/esm/model/commandHandlers/dashboard/common/stateInitializers.d.ts +4 -2
- package/esm/model/commandHandlers/dashboard/common/stateInitializers.d.ts.map +1 -1
- package/esm/model/commandHandlers/dashboard/common/stateInitializers.js +14 -10
- package/esm/model/commandHandlers/dashboard/initializeDashboardHandler/index.d.ts.map +1 -1
- package/esm/model/commandHandlers/dashboard/initializeDashboardHandler/index.js +9 -5
- package/esm/model/commandHandlers/dashboard/resetDashboardHandler.js +1 -1
- package/esm/model/commandHandlers/drill/common/mergeFilters.js +2 -2
- package/esm/model/commandHandlers/filterContext/tabSync/tabSyncRules.d.ts.map +1 -1
- package/esm/model/commandHandlers/filterContext/tabSync/tabSyncRules.js +114 -23
- package/esm/model/commandHandlers/parameters/loadParameterDependencies.d.ts +1 -1
- package/esm/model/commandHandlers/parameters/loadParameterDependencies.js +1 -1
- package/esm/model/commandHandlers/tabs/createDashboardTabHandler.d.ts.map +1 -1
- package/esm/model/commandHandlers/tabs/createDashboardTabHandler.js +3 -27
- package/esm/model/store/tabs/filterContext/filterSync.d.ts +6 -22
- package/esm/model/store/tabs/filterContext/filterSync.d.ts.map +1 -1
- package/esm/model/store/tabs/filterContext/filterSync.js +42 -40
- package/esm/model/types/commonTypes.d.ts +3 -0
- package/esm/model/types/commonTypes.d.ts.map +1 -1
- package/esm/presentation/automations/shared/automationFilters/automationParameters.d.ts +3 -3
- package/esm/presentation/automations/shared/automationFilters/automationParameters.js +3 -3
- package/esm/presentation/automations/shared/automationFilters/useAutomationAlertParameters.d.ts +1 -1
- package/esm/presentation/automations/shared/automationFilters/useAutomationAlertParameters.js +1 -1
- package/esm/presentation/dashboard/components/DensityInitializer.d.ts +1 -1
- package/esm/presentation/dashboard/components/DensityInitializer.js +1 -1
- package/esm/presentation/filterBar/measureValueFilter/useDashboardMeasureValueFilterData.d.ts +1 -1
- package/esm/presentation/localization/bundles/de-DE.localization-bundle.d.ts +0 -2
- package/esm/presentation/localization/bundles/de-DE.localization-bundle.d.ts.map +1 -1
- package/esm/presentation/localization/bundles/de-DE.localization-bundle.js +0 -2
- package/esm/presentation/localization/bundles/en-AU.localization-bundle.d.ts +0 -2
- package/esm/presentation/localization/bundles/en-AU.localization-bundle.d.ts.map +1 -1
- package/esm/presentation/localization/bundles/en-AU.localization-bundle.js +0 -2
- package/esm/presentation/localization/bundles/en-GB.localization-bundle.d.ts +0 -2
- package/esm/presentation/localization/bundles/en-GB.localization-bundle.d.ts.map +1 -1
- package/esm/presentation/localization/bundles/en-GB.localization-bundle.js +0 -2
- package/esm/presentation/localization/bundles/es-419.localization-bundle.d.ts +0 -2
- package/esm/presentation/localization/bundles/es-419.localization-bundle.d.ts.map +1 -1
- package/esm/presentation/localization/bundles/es-419.localization-bundle.js +0 -2
- package/esm/presentation/localization/bundles/es-ES.localization-bundle.d.ts +0 -2
- package/esm/presentation/localization/bundles/es-ES.localization-bundle.d.ts.map +1 -1
- package/esm/presentation/localization/bundles/es-ES.localization-bundle.js +0 -2
- package/esm/presentation/localization/bundles/fi-FI.localization-bundle.d.ts +0 -2
- package/esm/presentation/localization/bundles/fi-FI.localization-bundle.d.ts.map +1 -1
- package/esm/presentation/localization/bundles/fi-FI.localization-bundle.js +0 -2
- package/esm/presentation/localization/bundles/fr-CA.localization-bundle.d.ts +0 -2
- package/esm/presentation/localization/bundles/fr-CA.localization-bundle.d.ts.map +1 -1
- package/esm/presentation/localization/bundles/fr-CA.localization-bundle.js +0 -2
- package/esm/presentation/localization/bundles/fr-FR.localization-bundle.d.ts +0 -2
- package/esm/presentation/localization/bundles/fr-FR.localization-bundle.d.ts.map +1 -1
- package/esm/presentation/localization/bundles/fr-FR.localization-bundle.js +0 -2
- package/esm/presentation/localization/bundles/id-ID.localization-bundle.d.ts +0 -2
- package/esm/presentation/localization/bundles/id-ID.localization-bundle.d.ts.map +1 -1
- package/esm/presentation/localization/bundles/id-ID.localization-bundle.js +0 -2
- package/esm/presentation/localization/bundles/it-IT.localization-bundle.d.ts +0 -2
- package/esm/presentation/localization/bundles/it-IT.localization-bundle.d.ts.map +1 -1
- package/esm/presentation/localization/bundles/it-IT.localization-bundle.js +0 -2
- package/esm/presentation/localization/bundles/ja-JP.localization-bundle.d.ts +0 -2
- package/esm/presentation/localization/bundles/ja-JP.localization-bundle.d.ts.map +1 -1
- package/esm/presentation/localization/bundles/ja-JP.localization-bundle.js +0 -2
- package/esm/presentation/localization/bundles/ko-KR.localization-bundle.d.ts +0 -2
- package/esm/presentation/localization/bundles/ko-KR.localization-bundle.d.ts.map +1 -1
- package/esm/presentation/localization/bundles/ko-KR.localization-bundle.js +0 -2
- package/esm/presentation/localization/bundles/nl-NL.localization-bundle.d.ts +0 -2
- package/esm/presentation/localization/bundles/nl-NL.localization-bundle.d.ts.map +1 -1
- package/esm/presentation/localization/bundles/nl-NL.localization-bundle.js +0 -2
- package/esm/presentation/localization/bundles/pl-PL.localization-bundle.d.ts +0 -2
- package/esm/presentation/localization/bundles/pl-PL.localization-bundle.d.ts.map +1 -1
- package/esm/presentation/localization/bundles/pl-PL.localization-bundle.js +0 -2
- package/esm/presentation/localization/bundles/pt-BR.localization-bundle.d.ts +0 -2
- package/esm/presentation/localization/bundles/pt-BR.localization-bundle.d.ts.map +1 -1
- package/esm/presentation/localization/bundles/pt-BR.localization-bundle.js +0 -2
- package/esm/presentation/localization/bundles/pt-PT.localization-bundle.d.ts +0 -2
- package/esm/presentation/localization/bundles/pt-PT.localization-bundle.d.ts.map +1 -1
- package/esm/presentation/localization/bundles/pt-PT.localization-bundle.js +0 -2
- package/esm/presentation/localization/bundles/ru-RU.localization-bundle.d.ts +0 -2
- package/esm/presentation/localization/bundles/ru-RU.localization-bundle.d.ts.map +1 -1
- package/esm/presentation/localization/bundles/ru-RU.localization-bundle.js +0 -2
- package/esm/presentation/localization/bundles/sl-SI.localization-bundle.d.ts +0 -2
- package/esm/presentation/localization/bundles/sl-SI.localization-bundle.d.ts.map +1 -1
- package/esm/presentation/localization/bundles/sl-SI.localization-bundle.js +0 -2
- package/esm/presentation/localization/bundles/th-TH.localization-bundle.d.ts +0 -2
- package/esm/presentation/localization/bundles/th-TH.localization-bundle.d.ts.map +1 -1
- package/esm/presentation/localization/bundles/th-TH.localization-bundle.js +0 -2
- package/esm/presentation/localization/bundles/tr-TR.localization-bundle.d.ts +0 -2
- package/esm/presentation/localization/bundles/tr-TR.localization-bundle.d.ts.map +1 -1
- package/esm/presentation/localization/bundles/tr-TR.localization-bundle.js +0 -2
- package/esm/presentation/localization/bundles/uk-UA.localization-bundle.d.ts +0 -2
- package/esm/presentation/localization/bundles/uk-UA.localization-bundle.d.ts.map +1 -1
- package/esm/presentation/localization/bundles/uk-UA.localization-bundle.js +0 -2
- package/esm/presentation/localization/bundles/vi-VN.localization-bundle.d.ts +0 -2
- package/esm/presentation/localization/bundles/vi-VN.localization-bundle.d.ts.map +1 -1
- package/esm/presentation/localization/bundles/vi-VN.localization-bundle.js +0 -2
- package/esm/presentation/localization/bundles/zh-HK.localization-bundle.d.ts +0 -2
- package/esm/presentation/localization/bundles/zh-HK.localization-bundle.d.ts.map +1 -1
- package/esm/presentation/localization/bundles/zh-HK.localization-bundle.js +0 -2
- package/esm/presentation/localization/bundles/zh-Hans.localization-bundle.d.ts +0 -2
- package/esm/presentation/localization/bundles/zh-Hans.localization-bundle.d.ts.map +1 -1
- package/esm/presentation/localization/bundles/zh-Hans.localization-bundle.js +0 -2
- package/esm/presentation/localization/bundles/zh-Hant.localization-bundle.d.ts +0 -2
- package/esm/presentation/localization/bundles/zh-Hant.localization-bundle.d.ts.map +1 -1
- package/esm/presentation/localization/bundles/zh-Hant.localization-bundle.js +0 -2
- package/esm/sdk-ui-dashboard.d.ts +3 -0
- package/esm/types.d.ts +1 -1
- package/esm/types.js +1 -1
- package/package.json +26 -26
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stateInitializers.d.ts","sourceRoot":"","sources":["../../../../../src/model/commandHandlers/dashboard/common/stateInitializers.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,OAAO,
|
|
1
|
+
{"version":3,"file":"stateInitializers.d.ts","sourceRoot":"","sources":["../../../../../src/model/commandHandlers/dashboard/common/stateInitializers.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,OAAO,EACH,KAAK,wBAAwB,EAC7B,KAAK,8BAA8B,EAEtC,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAEH,KAAK,mCAAmC,EACxC,KAAK,UAAU,EACf,KAAK,yBAAyB,EAC9B,KAAK,+BAA+B,EAKpC,KAAK,sBAAsB,EAC3B,KAAK,iBAAiB,EAEtB,KAAK,wBAAwB,EAC7B,KAAK,QAAQ,EACb,KAAK,wBAAwB,EAC7B,KAAK,SAAS,EAGd,KAAK,MAAM,EAOd,MAAM,qBAAqB,CAAC;AAW7B,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,4CAA4C,CAAC;AAoB5E,OAAO,EACH,KAAK,gBAAgB,EAGxB,MAAM,+BAA+B,CAAC;AAiKvC,wBAAiB,+BAA+B,CAC5C,GAAG,EAAE,gBAAgB,EACrB,QAAQ,EAAE,SAAS,EACnB,gBAAgB,EAAE,iBAAiB,EACnC,YAAY,CAAC,EAAE,SAAS,CAAC,mCAAmC,CAAC,EAC7D,YAAY,CAAC,EAAE,MAAM,EACrB,mBAAmB,GAAE,wBAAwB,EAAO,GACrD,YAAY,CAAC;IACZ,WAAW,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;IACvC,SAAS,EAAE,UAAU,GAAG,SAAS,CAAC;IAClC,QAAQ,EAAE,QAAQ,EAAE,CAAC;CACxB,CAAC,CA6GD;AAwJD,wBAAgB,YAAY,CACxB,GAAG,EAAE,gBAAgB,EACrB,WAAW,EAAE,MAAM,EAAE,GACtB,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAInC;AAgGD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAiB,oCAAoC,CACjD,GAAG,EAAE,gBAAgB,EACrB,SAAS,EAAE,UAAU,EACrB,QAAQ,EAAE,QAAQ,EAAE,EACpB,QAAQ,EAAE,SAAS,EACnB,0CAA0C,EAAE,OAAO,EACnD,+BAA+B,EAAE,MAAM,CAAC,MAAM,EAAE,wBAAwB,GAAG,SAAS,CAAC,GAAG,SAAS,EAEjG,wBAAwB,EAAE,MAAM,CAAC,MAAM,EAAE,yBAAyB,EAAE,CAAC,GAAG,SAAS,EACjF,8BAA8B,EAAE,MAAM,CAAC,MAAM,EAAE,+BAA+B,EAAE,CAAC,EACjF,4BAA4B,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,EAC/D,0BAA0B,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,CAAC,EACrE,YAAY,CAAC,EAAE,SAAS,CAAC,mCAAmC,CAAC,EAC7D,kBAAkB,CAAC,EAAE,UAAU,EAC/B,wBAAwB,CAAC,EAAE,MAAM,EACjC,mBAAmB,GAAE,wBAAwB,EAAO,EACpD,0BAA0B,CAAC,EAAE,aAAa,CAAC,8BAA8B,CAAC,EAC1E,wBAAwB,CAAC,EAAE,wBAAwB,EAAE,GACtD,YAAY,CAAC,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CA6NzC"}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
// (C) 2021-2026 GoodData Corporation
|
|
2
2
|
import { cloneDeep, compact, isEmpty, update } from "lodash-es";
|
|
3
3
|
import { call, select } from "redux-saga/effects";
|
|
4
|
-
import { walkLayout } from "@gooddata/sdk-backend-spi";
|
|
4
|
+
import { walkLayout, } from "@gooddata/sdk-backend-spi";
|
|
5
5
|
import { areObjRefsEqual, isDashboardAttributeFilter, isDashboardDateFilterWithDimension, isIdentifierRef, isInsightWidget, isObjRef, } from "@gooddata/sdk-model";
|
|
6
6
|
import { dashboardFilterContextDefinition, dashboardFilterContextIdentity, } from "../../../../_staging/dashboard/dashboardFilterContext.js";
|
|
7
|
-
import { mergeFilterContextFilters, } from "../../../../_staging/dashboard/dashboardFilterContextValidation.js";
|
|
8
7
|
import { dashboardLayoutSanitize } from "../../../../_staging/dashboard/dashboardLayout.js";
|
|
9
8
|
import { createDefaultFilterContext } from "../../../../_staging/dashboard/defaultFilterContext.js";
|
|
10
9
|
import { DEFAULT_FISCAL_DATE_FILTER_PRESET } from "../../../../_staging/dateFilterConfig/defaultConfig.js";
|
|
@@ -26,6 +25,7 @@ import { resolveFilterDisplayForms } from "../../../utils/filterResolver.js";
|
|
|
26
25
|
import { EmptyDashboardLayout, dashboardInitialize } from "./dashboardInitialize.js";
|
|
27
26
|
import { loadAvailableDisplayFormRefs } from "./loadAvailableDisplayFormRefs.js";
|
|
28
27
|
import { mergedMigratedAttributeFilters } from "./migratedAttributeFilters.js";
|
|
28
|
+
import { mergeDashboardOverrideFilters, resolveOverrideFiltersByTab } from "./overrideFiltersAcrossTabs.js";
|
|
29
29
|
import { distributeParametersToTabs } from "./parameterHydration.js";
|
|
30
30
|
/**
|
|
31
31
|
* Processes a single tab's filterContext and returns initialized FilterContextState.
|
|
@@ -45,8 +45,9 @@ function* processExistingTabFilterContext(ctx, tab, effectiveDateFilterConfig, d
|
|
|
45
45
|
// Extract filter context definition or create a default filter context
|
|
46
46
|
const originalSanitizedFilterContextDefinition = dashboardFilterContextDefinition(tabAsDashboard, effectiveDateFilterConfig);
|
|
47
47
|
const filterContextIdentity = dashboardFilterContextIdentity(tabAsDashboard);
|
|
48
|
-
// Merge with override filters if provided
|
|
49
|
-
|
|
48
|
+
// Merge with override filters if provided. A common date of all time is the absence of the
|
|
49
|
+
// filter, so the merge also writes a common date override that has nothing to match.
|
|
50
|
+
const { mergedFilters, validationResults } = mergeDashboardOverrideFilters(originalSanitizedFilterContextDefinition.filters, overrideDefaultFilters ?? [], {
|
|
50
51
|
dateFilterConfig: tab.dateFilterConfig,
|
|
51
52
|
dateFilterConfigs: tab.dateFilterConfigs,
|
|
52
53
|
attributeFilterConfigs: tab.attributeFilterConfigs,
|
|
@@ -395,8 +396,10 @@ function getDisplayAsLabels(attributeFilterConfigs) {
|
|
|
395
396
|
* @param displayForms - specify display forms that should be used for in-memory resolution of
|
|
396
397
|
* attribute filter display forms to metadata objects
|
|
397
398
|
* @param persistedDashboard - dashboard to use for the persisted dashboard state slice in case it needs to be different from the dashboard param
|
|
399
|
+
* @param attributesWithReferences - attributes of the attribute filters, used to match an override filter
|
|
400
|
+
* to the same filter on another tab when filters persist across tabs
|
|
398
401
|
*/
|
|
399
|
-
export function* actionsToInitializeExistingDashboard(ctx, dashboard, insights, settings, isImmediateAttributeFilterMigrationEnabled, originalFilterContextDefinition, migratedAttributeFilters, migratedAttributeFilterConfigs, effectiveDateFilterConfigMap, tabsDateFilterConfigSource, displayForms, persistedDashboard, activeTabLocalIdentifier, workspaceParameters = [], unavailableObjectsOverride) {
|
|
402
|
+
export function* actionsToInitializeExistingDashboard(ctx, dashboard, insights, settings, isImmediateAttributeFilterMigrationEnabled, originalFilterContextDefinition, migratedAttributeFilters, migratedAttributeFilterConfigs, effectiveDateFilterConfigMap, tabsDateFilterConfigSource, displayForms, persistedDashboard, activeTabLocalIdentifier, workspaceParameters = [], unavailableObjectsOverride, attributesWithReferences) {
|
|
400
403
|
const effectiveActiveTabId = activeTabLocalIdentifier ?? dashboard.tabs?.[0]?.localIdentifier ?? DEFAULT_TAB_ID;
|
|
401
404
|
const effectiveProps = getEffectiveDashboardProperties(dashboard, effectiveActiveTabId);
|
|
402
405
|
const sanitizedDashboard = updateDashboard({
|
|
@@ -425,12 +428,13 @@ export function* actionsToInitializeExistingDashboard(ctx, dashboard, insights,
|
|
|
425
428
|
if (customizedDashboard?.tabs && customizedDashboard.tabs.length > 0) {
|
|
426
429
|
// Process each tab to build complete TabState with filterContext
|
|
427
430
|
const processedTabs = [];
|
|
431
|
+
const persistFiltersAcrossTabs = (settings.enableDashboardPersistentFiltersAcrossTabs ?? false) &&
|
|
432
|
+
dashboard.enablePersistentFiltersAcrossTabs === true;
|
|
433
|
+
// The tab the link opens on keeps the override filters as copied. When filters persist
|
|
434
|
+
// across tabs, every other tab receives those values on its own filters.
|
|
435
|
+
const overridesByTab = resolveOverrideFiltersByTab(customizedDashboard.tabs, ctx.config?.overrideDefaultFilters, effectiveActiveTabId, persistFiltersAcrossTabs, attributesWithReferences);
|
|
428
436
|
for (const tab of customizedDashboard.tabs) {
|
|
429
|
-
const
|
|
430
|
-
// Override default filters are only applied to the active tab
|
|
431
|
-
const overrideDefaultFilters = isActiveTab && ctx.config?.overrideDefaultFilters
|
|
432
|
-
? ctx.config?.overrideDefaultFilters
|
|
433
|
-
: undefined;
|
|
437
|
+
const overrideDefaultFilters = overridesByTab[tab.localIdentifier];
|
|
434
438
|
// Process this tab's filterContext
|
|
435
439
|
const [tabFilterContext, tabValidationResults] = yield call(processExistingTabFilterContext, ctx, tab, effectiveDateFilterConfigMap[tab.localIdentifier], dashboard.dataSets, overrideDefaultFilters, isImmediateAttributeFilterMigrationEnabled, migratedAttributeFilters?.[tab.localIdentifier] ?? [], originalFilterContextDefinition?.[tab.localIdentifier], displayForms, dashboard, unavailableObjectsOverride);
|
|
436
440
|
validationResults.push(...(tabValidationResults ?? []));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/model/commandHandlers/dashboard/initializeDashboardHandler/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,WAAW,EAAgB,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,OAAO,EACH,KAAK,wBAAwB,EAIhC,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACH,KAAK,UAAU,EAKf,KAAK,sBAAsB,EAK3B,KAAK,MAAM,EAOd,MAAM,qBAAqB,CAAC;AAQ7B,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AA0B1E,OAAO,EACH,KAAK,gBAAgB,EAGxB,MAAM,+BAA+B,CAAC;AA+JvC,KAAK,0BAA0B,GAAG;IAC9B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,wBAAwB,EAAE,wBAAwB,EAAE,CAAC;CACxD,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/model/commandHandlers/dashboard/initializeDashboardHandler/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,WAAW,EAAgB,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,OAAO,EACH,KAAK,wBAAwB,EAIhC,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACH,KAAK,UAAU,EAKf,KAAK,sBAAsB,EAK3B,KAAK,MAAM,EAOd,MAAM,qBAAqB,CAAC;AAQ7B,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AA0B1E,OAAO,EACH,KAAK,gBAAgB,EAGxB,MAAM,+BAA+B,CAAC;AA+JvC,KAAK,0BAA0B,GAAG;IAC9B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,wBAAwB,EAAE,wBAAwB,EAAE,CAAC;CACxD,CAAC;AAyYF,wBAAiB,cAAc,CAAC,GAAG,EAAE,gBAAgB,EAAE,GAAG,EAAE,mBAAmB;;;uIAyB9E;AAuED;;;;GAIG;AACH,wBAAiB,2BAA2B,CACxC,GAAG,EAAE,gBAAgB,EACrB,SAAS,EAAE,UAAU,EACrB,SAAS,CAAC,EAAE,0BAA0B;;;0IAiBzC;AAED,wBAAiB,qBAAqB,CAAC,GAAG,EAAE,gBAAgB,sNAW3D;AAqCD,wBAAiB,0BAA0B,CAAC,GAAG,EAAE,gBAAgB,EAAE,GAAG,EAAE,mBAAmB,GAAG,YAAY,CA0CzG"}
|
|
@@ -213,7 +213,7 @@ function* loadExistingDashboard(ctx, cmd, dashboardRef) {
|
|
|
213
213
|
const workspaceParametersList = Array.isArray(workspaceParameters) ? workspaceParameters : [];
|
|
214
214
|
const initActions = yield call(actionsToInitializeExistingDashboard, ctx, dashboard, insights, config.settings, config.settings.enableImmediateAttributeFilterDisplayAsLabelMigration ?? false, undefined, undefined, tabsAttributeFilterConfigs, tabsDateFilterConfig, tabsDateFilterConfigSource, createDisplayFormMap([], []),
|
|
215
215
|
// the default filter view changes only the working filters, never the persisted ones
|
|
216
|
-
cmd.payload.persistedDashboard ?? loadedDashboard, resolvedActiveTabId, workspaceParametersList);
|
|
216
|
+
cmd.payload.persistedDashboard ?? loadedDashboard, resolvedActiveTabId, workspaceParametersList, undefined, preloadedAttributeFilters?.attributesWithReferences);
|
|
217
217
|
const catalogPayload = {
|
|
218
218
|
dateHierarchyTemplates,
|
|
219
219
|
};
|
|
@@ -377,12 +377,16 @@ function makeCatalogParametersPayload(result) {
|
|
|
377
377
|
return { status: "loaded", parameters: result };
|
|
378
378
|
}
|
|
379
379
|
/**
|
|
380
|
-
* The attributes of the attribute filters, which match the filters of the tabs when
|
|
381
|
-
*
|
|
382
|
-
* so filters match on equal display form refs.
|
|
380
|
+
* The attributes of the attribute filters, which match the filters of the tabs when a selection is
|
|
381
|
+
* written to the other tabs: the default view of the active tab, or embed-url filter overrides.
|
|
382
|
+
* Nothing is shared, or the attributes fail to load: none, so filters match on equal display form refs.
|
|
383
383
|
*/
|
|
384
384
|
function* loadAttributesForSharedDefaultFilterView(ctx, dashboard, filterViews, renderMode, options, unavailableObjects) {
|
|
385
|
-
|
|
385
|
+
const shareOverrideFilters = !!options.persistFiltersAcrossTabs &&
|
|
386
|
+
(dashboard.tabs?.length ?? 0) > 1 &&
|
|
387
|
+
(ctx.config?.overrideDefaultFilters?.length ?? 0) > 0;
|
|
388
|
+
if (!shareOverrideFilters &&
|
|
389
|
+
!isDefaultFilterViewSharedAcrossTabs(dashboard, filterViews, renderMode, options)) {
|
|
386
390
|
return undefined;
|
|
387
391
|
}
|
|
388
392
|
const refs = attributeFilterDisplayFormRefs(dashboard, unavailableObjects);
|
|
@@ -142,7 +142,7 @@ function* resetDashboardFromPersisted(ctx, renderMode) {
|
|
|
142
142
|
const displayForms = yield select(selectAllCatalogDisplayFormsMap);
|
|
143
143
|
const resolvedInsightsValues = [...resolvedInsights.resolved.values()];
|
|
144
144
|
const workspaceParameters = yield select(selectCatalogParameters);
|
|
145
|
-
const initActions = yield call(actionsToInitializeExistingDashboard, ctx, dashboardWithUpdatedFilterContext, resolvedInsightsValues, settings, isImmediateAttributeFilterMigrationEnabled, effectiveOriginalFilterContext, migratedAttributeFilters, effectiveAttributeFilterConfigs, dateFilterConfig, tabsDateFilterConfigSource, displayForms, persistedDashboard, effectiveActiveTabId, workspaceParameters);
|
|
145
|
+
const initActions = yield call(actionsToInitializeExistingDashboard, ctx, dashboardWithUpdatedFilterContext, resolvedInsightsValues, settings, isImmediateAttributeFilterMigrationEnabled, effectiveOriginalFilterContext, migratedAttributeFilters, effectiveAttributeFilterConfigs, dateFilterConfig, tabsDateFilterConfigSource, displayForms, persistedDashboard, effectiveActiveTabId, workspaceParameters, undefined, attributesWithReferences);
|
|
146
146
|
batch = [
|
|
147
147
|
...initActions,
|
|
148
148
|
filterViewsActions.setDefaultFilterViewAppliedTabs(effectiveActiveTabId ? [effectiveActiveTabId] : []),
|
|
@@ -81,8 +81,8 @@ function intersectAttributeFilters(dashFilter, sourceFilter) {
|
|
|
81
81
|
* |---------------|---------------|-------------------------------------------|
|
|
82
82
|
* | Positive (A) | Positive (B) | Positive (A ∩ B) |
|
|
83
83
|
* | Negative (A) | Negative (B) | Negative (A ∪ B) |
|
|
84
|
-
* | Positive (A) | Negative (B) | Positive (A
|
|
85
|
-
* | Negative (A) | Positive (B) | Positive (B
|
|
84
|
+
* | Positive (A) | Negative (B) | Positive (A ∖ B) |
|
|
85
|
+
* | Negative (A) | Positive (B) | Positive (B ∖ A) |
|
|
86
86
|
*/
|
|
87
87
|
function intersectElements(aValues, aNegative, bValues, bNegative) {
|
|
88
88
|
if (!aNegative && !bNegative) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tabSyncRules.d.ts","sourceRoot":"","sources":["../../../../../src/model/commandHandlers/filterContext/tabSync/tabSyncRules.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tabSyncRules.d.ts","sourceRoot":"","sources":["../../../../../src/model/commandHandlers/filterContext/tabSync/tabSyncRules.ts"],"names":[],"mappings":"AAsCA,OAAO,EAA4B,KAAK,kBAAkB,EAAoB,MAAM,mBAAmB,CAAC;AAExG,KAAK,cAAc,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;AAwlB9C;;;;;GAKG;AACH,eAAO,MAAM,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,cAAc,CAG3D,CAAC"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { areObjRefsEqual, dashboardAttributeFilterItemLocalIdentifier, dashboardFilterLocalIdentifier, isAllTimeDashboardDateFilter, isDashboardAttributeFilter, isDashboardAttributeFilterItem, isDashboardCommonDateFilter, isDashboardDateFilter, isDashboardDateFilterWithDimension, isDashboardMeasureValueFilter, isNoopAllTimeDashboardDateFilter, } from "@gooddata/sdk-model";
|
|
3
3
|
import { applyMirroredFilterValue, findAppliedFilterForStaged, findCounterpartFilter, findOriginalFilter, isCrossFilteringVirtualFilter, retargetAttributeFilterItem, targetAcceptsAttributeFilterValue, } from "../../../store/tabs/filterContext/filterSync.js";
|
|
4
4
|
import { tabsActions } from "../../../store/tabs/index.js";
|
|
5
|
+
import { isDisplayFormMetadataReferencedBy } from "../../../utils/attributeFilterMatching.js";
|
|
5
6
|
function defineRule(actionCreator, rule) {
|
|
6
7
|
return defineActionsRule(actionCreator, (payload, target, ctx) => {
|
|
7
8
|
const targetPayload = rule(payload, target, ctx);
|
|
@@ -91,6 +92,35 @@ function mirroredListSelection(ctx, target, sourceLocalId, restoreTargetOriginal
|
|
|
91
92
|
? counterpart
|
|
92
93
|
: undefined;
|
|
93
94
|
}
|
|
95
|
+
function hasDisplayForm(counterpart, displayForm) {
|
|
96
|
+
return areObjRefsEqual(counterpart.attributeFilter.displayForm, displayForm);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Actions that let `counterpart` take `displayForm` and keep showing the label it shows now.
|
|
100
|
+
* The target tab learns the metadata of `displayForm`, and a counterpart without a display-as label
|
|
101
|
+
* gets its current display form as one. The display form itself is changed by the caller.
|
|
102
|
+
*/
|
|
103
|
+
function prepareDisplayFormChange(ctx, targetTabState, targetFilter, sourceDisplayForm) {
|
|
104
|
+
const actions = [];
|
|
105
|
+
const targetDisplayForms = targetTabState.filterContext?.attributeFilterDisplayForms;
|
|
106
|
+
const metadata = (ctx.source.filterContext?.attributeFilterDisplayForms ?? []).find((candidate) => isDisplayFormMetadataReferencedBy(candidate, sourceDisplayForm));
|
|
107
|
+
if (metadata &&
|
|
108
|
+
targetDisplayForms &&
|
|
109
|
+
!targetDisplayForms.some((candidate) => isDisplayFormMetadataReferencedBy(candidate, sourceDisplayForm))) {
|
|
110
|
+
actions.push(tabsActions.addAttributeFilterDisplayForm({
|
|
111
|
+
displayForm: metadata,
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
114
|
+
const localIdentifier = targetFilter.attributeFilter.localIdentifier;
|
|
115
|
+
const hasDisplayAsLabel = targetTabState.attributeFilterConfigs?.attributeFilterConfigs?.some((config) => config.localIdentifier === localIdentifier && config.displayAsLabel);
|
|
116
|
+
if (!hasDisplayAsLabel) {
|
|
117
|
+
actions.push(tabsActions.changeDisplayAsLabel({
|
|
118
|
+
localIdentifier,
|
|
119
|
+
displayAsLabel: targetFilter.attributeFilter.displayForm,
|
|
120
|
+
}));
|
|
121
|
+
}
|
|
122
|
+
return actions;
|
|
123
|
+
}
|
|
94
124
|
function findMeasureValueCounterpart(ctx, target, sourceLocalId) {
|
|
95
125
|
const sourceFilter = findFilterByLocalId(ctx.source, sourceLocalId);
|
|
96
126
|
if (!isDashboardMeasureValueFilter(sourceFilter)) {
|
|
@@ -112,14 +142,23 @@ function toUpsertDateFilterPayload(filter) {
|
|
|
112
142
|
if (isAllTimeDashboardDateFilter(filter)) {
|
|
113
143
|
return { type: "allTime", ...shared };
|
|
114
144
|
}
|
|
115
|
-
return {
|
|
145
|
+
return {
|
|
146
|
+
type,
|
|
147
|
+
granularity,
|
|
148
|
+
from,
|
|
149
|
+
to,
|
|
150
|
+
...(boundedFilter ? { boundedFilter } : {}),
|
|
151
|
+
...shared,
|
|
152
|
+
};
|
|
116
153
|
}
|
|
117
154
|
/**
|
|
118
155
|
* Saved date filter of `target`. A common date filter without a saved original is all time, which is
|
|
119
156
|
* stored as the absence of the filter. A dimension date filter without a saved original is left unchanged.
|
|
120
157
|
*/
|
|
121
158
|
function savedDateFilterPayload(target, applied, dataSet) {
|
|
122
|
-
const original = findOriginalFilter(target, applied ?? {
|
|
159
|
+
const original = findOriginalFilter(target, applied ?? {
|
|
160
|
+
dateFilter: { type: "relative", granularity: "GDC.time.date" },
|
|
161
|
+
});
|
|
123
162
|
if (isDashboardDateFilter(original)) {
|
|
124
163
|
return toUpsertDateFilterPayload(original);
|
|
125
164
|
}
|
|
@@ -168,7 +207,9 @@ const filterContextRules = [
|
|
|
168
207
|
if (!target.filterContext?.attributeFilterDisplayForms) {
|
|
169
208
|
return undefined;
|
|
170
209
|
}
|
|
171
|
-
return {
|
|
210
|
+
return {
|
|
211
|
+
displayForm: "displayForm" in payload ? payload.displayForm : payload,
|
|
212
|
+
};
|
|
172
213
|
}),
|
|
173
214
|
// Selection changes swap the display form together with elements expressed in it, so the swap
|
|
174
215
|
// follows the selection. Filter configuration handlers do not sync these actions.
|
|
@@ -191,7 +232,10 @@ const filterContextRules = [
|
|
|
191
232
|
}
|
|
192
233
|
const filterLocalId = dashboardFilterLocalIdentifier(counterpart);
|
|
193
234
|
if (!payload.restoreTargetOriginal) {
|
|
194
|
-
const changeDisplayForm = tabsActions.changeAttributeDisplayForm({
|
|
235
|
+
const changeDisplayForm = tabsActions.changeAttributeDisplayForm({
|
|
236
|
+
...payload,
|
|
237
|
+
filterLocalId,
|
|
238
|
+
});
|
|
195
239
|
const currentDisplayForm = counterpart.attributeFilter.displayForm;
|
|
196
240
|
const hasDisplayAsLabel = target.attributeFilterConfigs?.attributeFilterConfigs?.some((config) => config.localIdentifier === filterLocalId && config.displayAsLabel);
|
|
197
241
|
if (hasDisplayAsLabel || areObjRefsEqual(currentDisplayForm, payload.displayForm)) {
|
|
@@ -241,31 +285,51 @@ const filterContextRules = [
|
|
|
241
285
|
];
|
|
242
286
|
// Staged edits stay on their tab until applied. With `restoreTargetOriginal`, every tab restores its own saved value.
|
|
243
287
|
const selectionRules = [
|
|
244
|
-
|
|
288
|
+
// Elements are values of the display form of their filter, so the counterpart takes the display
|
|
289
|
+
// form of the written elements.
|
|
290
|
+
defineActionsRule(tabsActions.updateAttributeFilterSelection, (payload, target, ctx) => {
|
|
245
291
|
// Every tab migrates its own filters, so a migrated selection stays on its tab.
|
|
246
292
|
if (payload.isWorkingSelectionChange || payload.isResultOfMigration) {
|
|
247
|
-
return
|
|
293
|
+
return [];
|
|
248
294
|
}
|
|
249
295
|
const counterpart = mirroredListSelection(ctx, target, payload.filterLocalId, payload.restoreTargetOriginal);
|
|
250
296
|
if (!counterpart) {
|
|
251
|
-
return
|
|
297
|
+
return [];
|
|
252
298
|
}
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
|
|
299
|
+
const filterLocalId = counterpart.attributeFilter.localIdentifier;
|
|
300
|
+
const filter = payload.restoreTargetOriginal
|
|
301
|
+
? findOriginalFilter(target, counterpart)
|
|
302
|
+
: findFilterByLocalId(ctx.source, payload.filterLocalId);
|
|
303
|
+
if (!isDashboardAttributeFilter(filter)) {
|
|
304
|
+
return [];
|
|
256
305
|
}
|
|
257
|
-
|
|
306
|
+
const updateSelection = tabsActions.updateAttributeFilterSelection({
|
|
258
307
|
...payload,
|
|
259
|
-
filterLocalId
|
|
260
|
-
...(
|
|
308
|
+
filterLocalId,
|
|
309
|
+
...(payload.restoreTargetOriginal
|
|
261
310
|
? {
|
|
262
|
-
elements:
|
|
263
|
-
negativeSelection:
|
|
311
|
+
elements: filter.attributeFilter.attributeElements,
|
|
312
|
+
negativeSelection: filter.attributeFilter.negativeSelection,
|
|
264
313
|
}
|
|
265
314
|
: {}),
|
|
266
315
|
// Every tab migrates its own filters.
|
|
267
316
|
enableImmediateAttributeFilterDisplayAsLabelMigration: false,
|
|
268
|
-
};
|
|
317
|
+
});
|
|
318
|
+
const displayForm = filter.attributeFilter.displayForm;
|
|
319
|
+
if (hasDisplayForm(counterpart, displayForm)) {
|
|
320
|
+
return [updateSelection];
|
|
321
|
+
}
|
|
322
|
+
return [
|
|
323
|
+
// A restored filter takes back its own saved display form, which its tab already knows.
|
|
324
|
+
...(payload.restoreTargetOriginal
|
|
325
|
+
? []
|
|
326
|
+
: prepareDisplayFormChange(ctx, target, counterpart, displayForm)),
|
|
327
|
+
tabsActions.changeAttributeDisplayForm({
|
|
328
|
+
filterLocalId,
|
|
329
|
+
displayForm,
|
|
330
|
+
}),
|
|
331
|
+
updateSelection,
|
|
332
|
+
];
|
|
269
333
|
}),
|
|
270
334
|
defineRule(tabsActions.replaceAttributeFilterItem, (payload, target, ctx) => {
|
|
271
335
|
if (payload.isWorkingSelectionChange) {
|
|
@@ -292,7 +356,11 @@ const selectionRules = [
|
|
|
292
356
|
!targetAcceptsAttributeFilterValue(target, counterpart, replacement)) {
|
|
293
357
|
return undefined;
|
|
294
358
|
}
|
|
295
|
-
return {
|
|
359
|
+
return {
|
|
360
|
+
...payload,
|
|
361
|
+
filterLocalId,
|
|
362
|
+
filter: retargetAttributeFilterItem(replacement, filterLocalId),
|
|
363
|
+
};
|
|
296
364
|
}),
|
|
297
365
|
defineRule(tabsActions.clearAttributeFiltersSelection, (payload, target, ctx) => {
|
|
298
366
|
const filterLocalIds = payload.filterLocalIds.flatMap((filterLocalId) => {
|
|
@@ -302,7 +370,10 @@ const selectionRules = [
|
|
|
302
370
|
if (filterLocalIds.length === 0) {
|
|
303
371
|
return undefined;
|
|
304
372
|
}
|
|
305
|
-
return {
|
|
373
|
+
return {
|
|
374
|
+
filterLocalIds,
|
|
375
|
+
...(payload.restoreTargetOriginal ? { restoreOriginal: true } : {}),
|
|
376
|
+
};
|
|
306
377
|
}),
|
|
307
378
|
defineRule(tabsActions.upsertDateFilter, (payload, target) => {
|
|
308
379
|
if (payload.isWorkingSelectionChange || !target.filterContext?.filterContextDefinition) {
|
|
@@ -321,7 +392,10 @@ const selectionRules = [
|
|
|
321
392
|
}
|
|
322
393
|
// A common date filter created on the target does not take the local identifier of the source.
|
|
323
394
|
const ownValue = payload.restoreTargetOriginal ? value.localIdentifier : undefined;
|
|
324
|
-
return {
|
|
395
|
+
return {
|
|
396
|
+
...value,
|
|
397
|
+
localIdentifier: counterpart?.dateFilter.localIdentifier ?? ownValue,
|
|
398
|
+
};
|
|
325
399
|
}),
|
|
326
400
|
defineRule(tabsActions.changeMeasureValueFilterCondition, (payload, target, ctx) => {
|
|
327
401
|
if (payload.isWorkingSelectionChange) {
|
|
@@ -337,7 +411,11 @@ const selectionRules = [
|
|
|
337
411
|
}
|
|
338
412
|
const original = findOriginalFilter(target, counterpart);
|
|
339
413
|
return isDashboardMeasureValueFilter(original)
|
|
340
|
-
? {
|
|
414
|
+
? {
|
|
415
|
+
...payload,
|
|
416
|
+
localIdentifier,
|
|
417
|
+
conditions: original.dashboardMeasureValueFilter.conditions,
|
|
418
|
+
}
|
|
341
419
|
: undefined;
|
|
342
420
|
}),
|
|
343
421
|
defineRule(tabsActions.setMeasureValueFilterDimensionality, (payload, target, ctx) => {
|
|
@@ -363,13 +441,26 @@ const selectionRules = [
|
|
|
363
441
|
return values.length > 0 ? { ...payload, values } : undefined;
|
|
364
442
|
}),
|
|
365
443
|
// Other tabs receive the committed values of the source tab, not their own staged values.
|
|
366
|
-
|
|
444
|
+
// A committed list filter brings its display form, which the mirrored selection writes.
|
|
445
|
+
defineActionsRule(tabsActions.applyWorkingSelection, (_payload, target, ctx) => {
|
|
367
446
|
const filters = committedFilterValues(ctx, target);
|
|
368
447
|
const parameters = committedParameterValues(ctx, target);
|
|
369
448
|
if (filters.length === 0 && parameters.length === 0) {
|
|
370
|
-
return
|
|
449
|
+
return [];
|
|
371
450
|
}
|
|
372
|
-
|
|
451
|
+
const displayFormChanges = filters.filter(isDashboardAttributeFilter).flatMap((filter) => {
|
|
452
|
+
const targetFilter = findFilterByLocalId(target, filter.attributeFilter.localIdentifier);
|
|
453
|
+
return isDashboardAttributeFilter(targetFilter) &&
|
|
454
|
+
!hasDisplayForm(targetFilter, filter.attributeFilter.displayForm)
|
|
455
|
+
? prepareDisplayFormChange(ctx, target, targetFilter, filter.attributeFilter.displayForm)
|
|
456
|
+
: [];
|
|
457
|
+
});
|
|
458
|
+
return [
|
|
459
|
+
...displayFormChanges,
|
|
460
|
+
tabsActions.applyWorkingSelection({
|
|
461
|
+
mirroredSelection: { filters, parameters },
|
|
462
|
+
}),
|
|
463
|
+
];
|
|
373
464
|
}),
|
|
374
465
|
];
|
|
375
466
|
/**
|
|
@@ -5,7 +5,7 @@ interface IParameterDependenciesLoadResult {
|
|
|
5
5
|
failedRoots: IdentifierRef[];
|
|
6
6
|
}
|
|
7
7
|
/**
|
|
8
|
-
* Loads the root
|
|
8
|
+
* Loads the root -\> parameter dependencies of `roots` from the workspace references service.
|
|
9
9
|
*
|
|
10
10
|
* @remarks
|
|
11
11
|
* Walks the dependency graph in `direction: "down"` from each root, then collects the parameter
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { isUnexpectedResponseError } from "@gooddata/sdk-backend-spi";
|
|
3
3
|
import { insightRef, isIdentifierRef, serializeObjRef, } from "@gooddata/sdk-model";
|
|
4
4
|
/**
|
|
5
|
-
* Loads the root
|
|
5
|
+
* Loads the root -\> parameter dependencies of `roots` from the workspace references service.
|
|
6
6
|
*
|
|
7
7
|
* @remarks
|
|
8
8
|
* Walks the dependency graph in `direction: "down"` from each root, then collects the parameter
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createDashboardTabHandler.d.ts","sourceRoot":"","sources":["../../../../src/model/commandHandlers/tabs/createDashboardTabHandler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"createDashboardTabHandler.d.ts","sourceRoot":"","sources":["../../../../src/model/commandHandlers/tabs/createDashboardTabHandler.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAgB/C,OAAO,EAAE,KAAK,mBAAmB,EAAsB,MAAM,wBAAwB,CAAC;AAatF,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAyDnE;;GAEG;AACH,wBAAiB,yBAAyB,CAAC,GAAG,EAAE,gBAAgB,EAAE,GAAG,EAAE,mBAAmB,GAAG,YAAY,CAuExG"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// (C) 2025-2026 GoodData Corporation
|
|
2
|
-
import { cloneDeep } from "lodash-es";
|
|
3
2
|
import { call, put, select } from "redux-saga/effects";
|
|
4
3
|
import { createDefaultFilterContext } from "../../../_staging/dashboard/defaultFilterContext.js";
|
|
5
4
|
import { DEFAULT_FISCAL_DATE_FILTER_PRESET } from "../../../_staging/dateFilterConfig/defaultConfig.js";
|
|
@@ -10,16 +9,13 @@ import { dashboardTabCreated } from "../../events/tabs.js";
|
|
|
10
9
|
import { dispatchDashboardEvent } from "../../store/_infra/eventDispatcher.js";
|
|
11
10
|
import { InitialUndoState } from "../../store/_infra/undoEnhancer.js";
|
|
12
11
|
import { selectActiveCalendars, selectDateFilterConfig, selectSettings, } from "../../store/config/configSelectors.js";
|
|
13
|
-
import { selectCrossFilteringFiltersLocalIdentifiersByTab } from "../../store/drill/drillSelectors.js";
|
|
14
|
-
import { selectEnablePersistentFiltersAcrossTabs } from "../../store/meta/metaSelectors.js";
|
|
15
|
-
import { seedSyncedTabFilters } from "../../store/tabs/filterContext/filterSync.js";
|
|
16
12
|
import { tabsActions } from "../../store/tabs/index.js";
|
|
17
13
|
import { selectScreen } from "../../store/tabs/layout/layoutSelectors.js";
|
|
18
14
|
import { selectActiveTabLocalIdentifier, selectTabs } from "../../store/tabs/tabsSelectors.js";
|
|
19
15
|
import { generateTabLocalIdentifier } from "../../utils/tabLocalIdentifier.js";
|
|
20
16
|
import { EmptyDashboardLayout } from "../dashboard/common/dashboardInitialize.js";
|
|
21
17
|
import { switchDashboardTabHandler } from "./switchDashboardTabHandler.js";
|
|
22
|
-
const getTabState = ({ title, localIdentifier, isRenaming, filterContext,
|
|
18
|
+
const getTabState = ({ title, localIdentifier, isRenaming, filterContext, dateFilterConfig, dateFilterConfigs, attributeFilterConfigs, layout, screen, }) => {
|
|
23
19
|
return {
|
|
24
20
|
title,
|
|
25
21
|
localIdentifier,
|
|
@@ -27,8 +23,6 @@ const getTabState = ({ title, localIdentifier, isRenaming, filterContext, origin
|
|
|
27
23
|
filterContext: {
|
|
28
24
|
filtersWithInvalidSelection: [],
|
|
29
25
|
filterContextDefinition: filterContext,
|
|
30
|
-
originalFilterContextDefinition: originalFilterContext,
|
|
31
|
-
attributeFilterDisplayForms,
|
|
32
26
|
},
|
|
33
27
|
dateFilterConfig: {
|
|
34
28
|
dateFilterConfig: undefined,
|
|
@@ -42,10 +36,6 @@ const getTabState = ({ title, localIdentifier, isRenaming, filterContext, origin
|
|
|
42
36
|
attributeFilterConfigs: {
|
|
43
37
|
attributeFilterConfigs,
|
|
44
38
|
},
|
|
45
|
-
measureValueFilterConfigs: {
|
|
46
|
-
measureValueFilterConfigs,
|
|
47
|
-
},
|
|
48
|
-
filterGroupsConfig,
|
|
49
39
|
layout: {
|
|
50
40
|
layout: layout,
|
|
51
41
|
stash: {},
|
|
@@ -68,7 +58,7 @@ export function* createDashboardTabHandler(ctx, cmd) {
|
|
|
68
58
|
const activeCalendars = yield select(selectActiveCalendars);
|
|
69
59
|
const screen = yield select(selectScreen);
|
|
70
60
|
const updatedTabs = (tabs ?? []).slice();
|
|
71
|
-
// 2. Create new tab with an empty
|
|
61
|
+
// 2. Create new tab with an empty filter context and layout
|
|
72
62
|
const effectiveDateFilterConfig = activeCalendars?.fiscal &&
|
|
73
63
|
activeCalendars?.default === "FISCAL" &&
|
|
74
64
|
configHasFiscalPresets(dateFilterConfig)
|
|
@@ -76,27 +66,13 @@ export function* createDashboardTabHandler(ctx, cmd) {
|
|
|
76
66
|
: dateFilterConfig;
|
|
77
67
|
const newTabId = generateTabLocalIdentifier();
|
|
78
68
|
const newTabFilterContext = createDefaultFilterContext(effectiveDateFilterConfig, true);
|
|
79
|
-
// Synced writes only update existing counterparts and reset needs originals to restore to,
|
|
80
|
-
// so a tab created while sync is on must start with the synced filters as its saved state.
|
|
81
|
-
const activeTab = tabs?.find((tab) => tab.localIdentifier === activeTabLocalIdentifier);
|
|
82
|
-
const crossFilteringLocalIdsByTab = yield select(selectCrossFilteringFiltersLocalIdentifiersByTab);
|
|
83
|
-
const enablePersistentFiltersAcrossTabs = yield select(selectEnablePersistentFiltersAcrossTabs);
|
|
84
|
-
const seed = enablePersistentFiltersAcrossTabs && activeTab
|
|
85
|
-
? seedSyncedTabFilters(activeTab, newTabFilterContext, crossFilteringLocalIdsByTab)
|
|
86
|
-
: undefined;
|
|
87
69
|
const newTab = getTabState({
|
|
88
70
|
localIdentifier: newTabId,
|
|
89
71
|
title,
|
|
90
72
|
layout: EmptyDashboardLayout,
|
|
91
73
|
screen,
|
|
92
|
-
filterContext:
|
|
93
|
-
originalFilterContext: seed ? cloneDeep(seed.filterContextDefinition) : undefined,
|
|
94
|
-
attributeFilterDisplayForms: seed?.attributeFilterDisplayForms,
|
|
74
|
+
filterContext: newTabFilterContext,
|
|
95
75
|
dateFilterConfig,
|
|
96
|
-
dateFilterConfigs: seed?.dateFilterConfigs,
|
|
97
|
-
attributeFilterConfigs: seed?.attributeFilterConfigs,
|
|
98
|
-
measureValueFilterConfigs: seed?.measureValueFilterConfigs,
|
|
99
|
-
filterGroupsConfig: seed?.filterGroupsConfig,
|
|
100
76
|
isRenaming: shouldStartRenaming,
|
|
101
77
|
});
|
|
102
78
|
// 4. Insert new tab at specified index (or append)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type IAttributeWithReferences } from "@gooddata/sdk-backend-spi";
|
|
2
|
-
import { type DashboardAttributeFilterItem, type FilterContextItem, type
|
|
2
|
+
import { type DashboardAttributeFilterItem, type FilterContextItem, type ObjRef, type ParameterValue } from "@gooddata/sdk-model";
|
|
3
3
|
import { type ITabState } from "../tabsState.js";
|
|
4
4
|
import { type WorkingFilterContextItem } from "./filterContextState.js";
|
|
5
5
|
type StagedFilter = FilterContextItem | WorkingFilterContextItem;
|
|
@@ -63,6 +63,7 @@ export declare function preserveFilterDependencies(existing: DashboardAttributeF
|
|
|
63
63
|
/**
|
|
64
64
|
* Copies an applied source filter onto an existing target filter, keeping the target's
|
|
65
65
|
* local identifier, parent dependencies, and measure-value dimensionality. Does not create a filter.
|
|
66
|
+
* A list filter takes the display form of the source, because its elements are values of that display form.
|
|
66
67
|
*
|
|
67
68
|
* @internal
|
|
68
69
|
*/
|
|
@@ -99,27 +100,6 @@ export declare function writeMirroredSelection(tab: ITabState, selection: IMirro
|
|
|
99
100
|
* @internal
|
|
100
101
|
*/
|
|
101
102
|
export declare function findAppliedFilterForStaged(tab: ITabState, staged: StagedFilter): FilterContextItem | undefined;
|
|
102
|
-
/**
|
|
103
|
-
* Filter state of a tab created while sync is on.
|
|
104
|
-
*
|
|
105
|
-
* @internal
|
|
106
|
-
*/
|
|
107
|
-
export interface ISyncedTabSeed {
|
|
108
|
-
readonly filterContextDefinition: IFilterContextDefinition;
|
|
109
|
-
readonly attributeFilterDisplayForms: IAttributeDisplayFormMetadataObject[];
|
|
110
|
-
readonly attributeFilterConfigs: IDashboardAttributeFilterConfig[];
|
|
111
|
-
readonly dateFilterConfigs: IDashboardDateFilterConfigItem[];
|
|
112
|
-
readonly measureValueFilterConfigs: IDashboardMeasureValueFilterConfig[];
|
|
113
|
-
readonly filterGroupsConfig: IDashboardFilterGroupsConfig | undefined;
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* Seeds a new tab with the applied filters of `sourceTab` and the configs that belong to them,
|
|
117
|
-
* so every synced filter has a counterpart on the new tab. Cross-filtering virtual filters stay
|
|
118
|
-
* on their own tab. `base` provides the title and description of the new filter context.
|
|
119
|
-
*
|
|
120
|
-
* @internal
|
|
121
|
-
*/
|
|
122
|
-
export declare function seedSyncedTabFilters(sourceTab: ITabState, base: IFilterContextDefinition, crossFilteringLocalIdsByTab?: CrossFilteringLocalIdsByTab): ISyncedTabSeed;
|
|
123
103
|
/**
|
|
124
104
|
* Whether `value` can be stored on `targetFilter`.
|
|
125
105
|
*
|
|
@@ -128,6 +108,10 @@ export declare function seedSyncedTabFilters(sourceTab: ITabState, base: IFilter
|
|
|
128
108
|
* list-or-text filter, and cannot be applied to a list-only filter. An omitted selection type
|
|
129
109
|
* means list-or-text, except a single-value list filter, which means list.
|
|
130
110
|
*
|
|
111
|
+
* A text value is a value of one label, so it is valid only on a filter of the same display form.
|
|
112
|
+
* A filter that shows that label through its display-as label filters by another label and keeps
|
|
113
|
+
* its own value.
|
|
114
|
+
*
|
|
131
115
|
* @internal
|
|
132
116
|
*/
|
|
133
117
|
export declare function targetAcceptsAttributeFilterValue(targetTab: ITabState, targetFilter: DashboardAttributeFilterItem, value: DashboardAttributeFilterItem): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filterSync.d.ts","sourceRoot":"","sources":["../../../../../src/model/store/tabs/filterContext/filterSync.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAC1E,OAAO,EACH,KAAK,4BAA4B,EACjC,KAAK,iBAAiB,
|
|
1
|
+
{"version":3,"file":"filterSync.d.ts","sourceRoot":"","sources":["../../../../../src/model/store/tabs/filterContext/filterSync.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAC1E,OAAO,EACH,KAAK,4BAA4B,EACjC,KAAK,iBAAiB,EAItB,KAAK,MAAM,EACX,KAAK,cAAc,EAgBtB,MAAM,qBAAqB,CAAC;AAI7B,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AAExE,KAAK,YAAY,GAAG,iBAAiB,GAAG,wBAAwB,CAAC;AAEjE;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACpC;;OAEG;IACH,QAAQ,CAAC,2BAA2B,CAAC,EAAE,2BAA2B,CAAC;CACtE;AAED;;;;;GAKG;AACH,MAAM,MAAM,2BAA2B,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC,CAAC;AAEtF;;;;;GAKG;AACH,wBAAgB,6BAA6B,CACzC,GAAG,EAAE,SAAS,EACd,eAAe,EAAE,MAAM,GAAG,SAAS,EACnC,2BAA2B,EAAE,2BAA2B,GAAG,SAAS,GACrE,OAAO,CAKT;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CACjC,SAAS,EAAE,SAAS,EACpB,YAAY,EAAE,iBAAiB,EAC/B,SAAS,EAAE,SAAS,EACpB,wBAAwB,CAAC,EAAE,wBAAwB,EAAE,EACrD,OAAO,CAAC,EAAE,uBAAuB,GAClC,iBAAiB,GAAG,SAAS,CA2C/B;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAC9B,GAAG,EAAE,SAAS,EACd,aAAa,EAAE,iBAAiB,GACjC,iBAAiB,GAAG,SAAS,CAyB/B;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CACvC,MAAM,EAAE,4BAA4B,EACpC,eAAe,EAAE,MAAM,GACxB,4BAA4B,CAuB9B;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACtC,QAAQ,EAAE,4BAA4B,EACtC,WAAW,EAAE,4BAA4B,GAC1C,4BAA4B,CA6B9B;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACpC,MAAM,EAAE,iBAAiB,EACzB,MAAM,EAAE,iBAAiB,GAC1B,iBAAiB,CAyDnB;AAED;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IAC/B,QAAQ,CAAC,OAAO,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAC/C,QAAQ,CAAC,UAAU,EAAE,SAAS,uBAAuB,EAAE,CAAC;CAC3D;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACpC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,cAAc,GAAG,SAAS,CAAC;CAC9C;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE,kBAAkB,GAAG,IAAI,CAuB1F;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACtC,GAAG,EAAE,SAAS,EACd,MAAM,EAAE,YAAY,GACrB,iBAAiB,GAAG,SAAS,CAiB/B;AA4GD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iCAAiC,CAC7C,SAAS,EAAE,SAAS,EACpB,YAAY,EAAE,4BAA4B,EAC1C,KAAK,EAAE,4BAA4B,GACpC,OAAO,CAiBT"}
|