diff --git a/common/changes/@gooddata/sdk-ui-all/codex-f1-3001-line-series-limit_2026-10-30-09-40-00.json b/common/changes/@gooddata/sdk-ui-all/codex-f1-3001-line-series-limit_2026-10-30-09-40-00.json new file mode 100644 index 00000000000..f6a11c1aa28 --- /dev/null +++ b/common/changes/@gooddata/sdk-ui-all/codex-f1-3001-line-series-limit_2026-10-30-09-40-00.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "comment": "sdk-ui-charts: Prevent line charts with more than 1,000 series from freezing the browser by showing the data size error before rendering.", + "type": "patch", + "packageName": "@gooddata/sdk-ui-all" + } + ], + "packageName": "@gooddata/sdk-ui-all" +} diff --git a/common/changes/@gooddata/sdk-ui-all/ine-cq-3220_2026-11-02-09-00-00.json b/common/changes/@gooddata/sdk-ui-all/ine-cq-3220_2026-11-02-09-00-00.json new file mode 100644 index 00000000000..255ff1a4ec1 --- /dev/null +++ b/common/changes/@gooddata/sdk-ui-all/ine-cq-3220_2026-11-02-09-00-00.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@gooddata/sdk-ui-all", + "comment": "fix: AaC query preview now honors date filter `empty_values` and sends All time as `allTimeDateFilter`.", + "type": "none" + } + ], + "packageName": "@gooddata/sdk-ui-all" +} diff --git a/common/changes/@gooddata/sdk-ui-all/jimi-STL-3478-remove-amplitude-leftovers_2026-11-02-10-00.json b/common/changes/@gooddata/sdk-ui-all/jimi-STL-3478-remove-amplitude-leftovers_2026-11-02-10-00.json new file mode 100644 index 00000000000..822f16dc74e --- /dev/null +++ b/common/changes/@gooddata/sdk-ui-all/jimi-STL-3478-remove-amplitude-leftovers_2026-11-02-10-00.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@gooddata/sdk-ui-all", + "comment": "api-client-tiger: Removed the AmplitudeService type and the amplitude property of TelemetryServices. The backend no longer returns Amplitude telemetry configuration in the user profile.", + "type": "none" + } + ], + "packageName": "@gooddata/sdk-ui-all" +} diff --git a/common/config/rush/version-policies.json b/common/config/rush/version-policies.json index c08089e3592..b5436089efc 100644 --- a/common/config/rush/version-policies.json +++ b/common/config/rush/version-policies.json @@ -2,14 +2,14 @@ { "definitionName": "lockStepVersion", "policyName": "sdk", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "nextBump": "prerelease", "mainProject": "@gooddata/sdk-ui-all" }, { "definitionName": "lockStepVersion", "policyName": "sdk-not-npm-publish", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "nextBump": "prerelease", "mainProject": "@gooddata/sdk-ui-all" } diff --git a/examples/playground/package.json b/examples/playground/package.json index 3dd82c0a574..bb04046b7dd 100644 --- a/examples/playground/package.json +++ b/examples/playground/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/playground", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "private": true, "description": "GoodData.UI SDK - Developer Playground", "license": "LicenseRef-LICENSE", diff --git a/examples/sdk-interactive-examples/examples-template/package.json b/examples/sdk-interactive-examples/examples-template/package.json index eda071e65be..767e5aa9e9f 100644 --- a/examples/sdk-interactive-examples/examples-template/package.json +++ b/examples/sdk-interactive-examples/examples-template/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-interactive-examples-template", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "private": false, "description": "GoodData interactive example template", "license": "LicenseRef-LICENSE", diff --git a/examples/sdk-interactive-examples/examples/example-attributefilter/package.json b/examples/sdk-interactive-examples/examples/example-attributefilter/package.json index 748584d92bb..9517501d8d5 100644 --- a/examples/sdk-interactive-examples/examples/example-attributefilter/package.json +++ b/examples/sdk-interactive-examples/examples/example-attributefilter/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-interactive-example-attributefilter", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "private": false, "description": "This example demonstrates how to use the AttributeFilter component to filter data in a visualization.", "license": "LicenseRef-LICENSE", diff --git a/examples/sdk-interactive-examples/examples/example-chartconfig/package.json b/examples/sdk-interactive-examples/examples/example-chartconfig/package.json index 6404230325a..941586fbdf3 100644 --- a/examples/sdk-interactive-examples/examples/example-chartconfig/package.json +++ b/examples/sdk-interactive-examples/examples/example-chartconfig/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-interactive-example-chartconfig", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "private": false, "description": "This interactive example demonstrates how to manipulate the chart config.", "license": "LicenseRef-LICENSE", diff --git a/examples/sdk-interactive-examples/examples/example-columnchart/package.json b/examples/sdk-interactive-examples/examples/example-columnchart/package.json index b05748b18fa..666dc22a0e8 100644 --- a/examples/sdk-interactive-examples/examples/example-columnchart/package.json +++ b/examples/sdk-interactive-examples/examples/example-columnchart/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-interactive-example-columnchart", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "private": false, "description": "This example demonstrates the usage of the ColumnChart component with the viewBy and stackBy properties.", "license": "LicenseRef-LICENSE", diff --git a/examples/sdk-interactive-examples/examples/example-combochart/package.json b/examples/sdk-interactive-examples/examples/example-combochart/package.json index 48b1e900675..06041367f9b 100644 --- a/examples/sdk-interactive-examples/examples/example-combochart/package.json +++ b/examples/sdk-interactive-examples/examples/example-combochart/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-interactive-example-combochart", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "private": false, "description": "Example demonstrates ComboChart secondaryMeasures definition. ", "license": "LicenseRef-LICENSE", diff --git a/examples/sdk-interactive-examples/examples/example-dashboard/package.json b/examples/sdk-interactive-examples/examples/example-dashboard/package.json index 5d029aedca6..081e7ff4d6b 100644 --- a/examples/sdk-interactive-examples/examples/example-dashboard/package.json +++ b/examples/sdk-interactive-examples/examples/example-dashboard/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-interactive-example-dashboard", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "private": false, "description": "This example shows how to use the Dashboard component.", "license": "LicenseRef-LICENSE", diff --git a/examples/sdk-interactive-examples/examples/example-datefilter/package.json b/examples/sdk-interactive-examples/examples/example-datefilter/package.json index 6fe28fed05e..a63eb1627aa 100644 --- a/examples/sdk-interactive-examples/examples/example-datefilter/package.json +++ b/examples/sdk-interactive-examples/examples/example-datefilter/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-interactive-example-datefilter", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "private": false, "description": "Example demonstrates usage of Date Filter component.", "license": "LicenseRef-LICENSE", diff --git a/examples/sdk-interactive-examples/examples/example-dependentfilters/package.json b/examples/sdk-interactive-examples/examples/example-dependentfilters/package.json index a0ff993a835..21ef798cfb4 100644 --- a/examples/sdk-interactive-examples/examples/example-dependentfilters/package.json +++ b/examples/sdk-interactive-examples/examples/example-dependentfilters/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-interactive-example-dependentfilters", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "private": false, "description": "This example demonstrates how to use multiple attribute filters linked together to filter data in a visualization.", "license": "LicenseRef-LICENSE", diff --git a/examples/sdk-interactive-examples/examples/example-execute/package.json b/examples/sdk-interactive-examples/examples/example-execute/package.json index 7df0ac3b7d5..427fffa5aeb 100644 --- a/examples/sdk-interactive-examples/examples/example-execute/package.json +++ b/examples/sdk-interactive-examples/examples/example-execute/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-interactive-example-execute", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "private": false, "description": "This example demonstrates using Execute component and build custom visualization.", "license": "LicenseRef-LICENSE", diff --git a/examples/sdk-interactive-examples/examples/example-granularity/package.json b/examples/sdk-interactive-examples/examples/example-granularity/package.json index 0fb5842469d..4c7450786f1 100644 --- a/examples/sdk-interactive-examples/examples/example-granularity/package.json +++ b/examples/sdk-interactive-examples/examples/example-granularity/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-interactive-example-granularity", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "private": false, "description": "This example exmplains DateFilter granularity ", "license": "LicenseRef-LICENSE", diff --git a/examples/sdk-interactive-examples/examples/example-headline/package.json b/examples/sdk-interactive-examples/examples/example-headline/package.json index d193c6a7ed6..e7bf249e57f 100644 --- a/examples/sdk-interactive-examples/examples/example-headline/package.json +++ b/examples/sdk-interactive-examples/examples/example-headline/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-interactive-example-headline", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "private": false, "description": "This example shows how to use the Headline component.", "license": "LicenseRef-LICENSE", diff --git a/examples/sdk-interactive-examples/examples/example-pivottable/package.json b/examples/sdk-interactive-examples/examples/example-pivottable/package.json index b6ba1312fc4..203e078aa2f 100644 --- a/examples/sdk-interactive-examples/examples/example-pivottable/package.json +++ b/examples/sdk-interactive-examples/examples/example-pivottable/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-interactive-example-pivottable", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "private": false, "description": "Basic PivotTable manipulation.", "license": "LicenseRef-LICENSE", diff --git a/examples/sdk-interactive-examples/examples/example-relativedatefilter/package.json b/examples/sdk-interactive-examples/examples/example-relativedatefilter/package.json index dcc5f4427d0..3c70521aee2 100644 --- a/examples/sdk-interactive-examples/examples/example-relativedatefilter/package.json +++ b/examples/sdk-interactive-examples/examples/example-relativedatefilter/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-interactive-example-relativedatefilter", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "private": false, "description": "Example demonstrates how to set relative DateFilter for visualization.", "license": "LicenseRef-LICENSE", diff --git a/examples/sdk-interactive-examples/examples/example-repeater/package.json b/examples/sdk-interactive-examples/examples/example-repeater/package.json index ac20fa4cea6..aea56b9fb93 100644 --- a/examples/sdk-interactive-examples/examples/example-repeater/package.json +++ b/examples/sdk-interactive-examples/examples/example-repeater/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-interactive-example-repeater", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "private": false, "description": "This example demonstrates how to use Repeater component.", "license": "LicenseRef-LICENSE", diff --git a/examples/sdk-interactive-examples/package.json b/examples/sdk-interactive-examples/package.json index 6def4654040..500b228ddf7 100644 --- a/examples/sdk-interactive-examples/package.json +++ b/examples/sdk-interactive-examples/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-interactive-examples", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "private": false, "description": "GoodData React interactive examples", "license": "LicenseRef-LICENSE", diff --git a/libs/api-client-tiger/api/api-client-tiger.api.md b/libs/api-client-tiger/api/api-client-tiger.api.md index d850167ffb9..c3388eb2957 100644 --- a/libs/api-client-tiger/api/api-client-tiger.api.md +++ b/libs/api-client-tiger/api/api-client-tiger.api.md @@ -27249,12 +27249,12 @@ interface IAnalyticalDashboardCommonProps { // (undocumented) disableFilterViews?: boolean; // (undocumented) - disablePersistentFiltersAcrossTabs?: boolean; - // (undocumented) disableUserFilterReset?: boolean; // (undocumented) disableUserFilterSave?: boolean; // (undocumented) + enablePersistentFiltersAcrossTabs?: boolean; + // (undocumented) evaluationFrequency?: string; // (undocumented) plugins?: IDashboardPluginLink[]; diff --git a/libs/api-client-tiger/package.json b/libs/api-client-tiger/package.json index afd9b097a67..9b9a6b1058a 100644 --- a/libs/api-client-tiger/package.json +++ b/libs/api-client-tiger/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/api-client-tiger", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "API Client for GoodData Cloud and GoodData.CN", "license": "MIT", "author": "GoodData", diff --git a/libs/api-client-tiger/src/gd-tiger-model/AnalyticalDashboardModelV2.ts b/libs/api-client-tiger/src/gd-tiger-model/AnalyticalDashboardModelV2.ts index 3834e7dd561..671b5b34680 100644 --- a/libs/api-client-tiger/src/gd-tiger-model/AnalyticalDashboardModelV2.ts +++ b/libs/api-client-tiger/src/gd-tiger-model/AnalyticalDashboardModelV2.ts @@ -45,7 +45,7 @@ export interface IAnalyticalDashboardCommonProps { disableUserFilterReset?: boolean; disableUserFilterSave?: boolean; disableFilterViews?: boolean; - disablePersistentFiltersAcrossTabs?: boolean; + enablePersistentFiltersAcrossTabs?: boolean; evaluationFrequency?: string; sectionHeadersDateDataSet?: ObjRef; timezoneConfig?: IDashboardTimezoneConfig; diff --git a/libs/api-client-tiger/src/generated/metadata-json-api/api.ts b/libs/api-client-tiger/src/generated/metadata-json-api/api.ts index 478d60e781c..c66f698a2be 100644 --- a/libs/api-client-tiger/src/generated/metadata-json-api/api.ts +++ b/libs/api-client-tiger/src/generated/metadata-json-api/api.ts @@ -280,28 +280,6 @@ export interface AllTimeDateFilterAllTimeDateFilter { export type AllTimeDateFilterAllTimeDateFilterEmptyValueHandlingEnum = 'INCLUDE' | 'EXCLUDE' | 'ONLY'; export type AllTimeDateFilterAllTimeDateFilterGranularityEnum = 'SECOND' | 'SECOND_OF_MINUTE' | 'SECOND_OF_DAY' | 'MINUTE' | 'MINUTE_OF_HOUR' | 'MINUTE_OF_DAY' | 'HOUR' | 'HOUR_OF_DAY' | 'DAY' | 'DAY_OF_WEEK' | 'DAY_OF_MONTH' | 'DAY_OF_QUARTER' | 'DAY_OF_YEAR' | 'WEEK' | 'WEEK_OF_YEAR' | 'MONTH' | 'MONTH_OF_YEAR' | 'QUARTER' | 'QUARTER_OF_YEAR' | 'YEAR' | 'FISCAL_DAY_OF_FISCAL_WEEK' | 'FISCAL_DAY_OF_FISCAL_MONTH' | 'FISCAL_DAY_OF_FISCAL_QUARTER' | 'FISCAL_DAY_OF_FISCAL_SEMESTER' | 'FISCAL_DAY_OF_FISCAL_YEAR' | 'FISCAL_WEEK' | 'FISCAL_WEEK_OF_FISCAL_MONTH' | 'FISCAL_WEEK_OF_FISCAL_QUARTER' | 'FISCAL_WEEK_OF_FISCAL_SEMESTER' | 'FISCAL_WEEK_OF_FISCAL_YEAR' | 'FISCAL_MONTH' | 'FISCAL_MONTH_OF_FISCAL_QUARTER' | 'FISCAL_MONTH_OF_FISCAL_SEMESTER' | 'FISCAL_MONTH_OF_FISCAL_YEAR' | 'FISCAL_QUARTER' | 'FISCAL_QUARTER_OF_FISCAL_SEMESTER' | 'FISCAL_QUARTER_OF_FISCAL_YEAR' | 'FISCAL_SEMESTER' | 'FISCAL_SEMESTER_OF_FISCAL_YEAR' | 'FISCAL_YEAR'; -/** - * Amplitude service. - */ -export interface AmplitudeService { - /** - * API key for AI project - intended for frontend use. - */ - 'aiProjectApiKey': string; - /** - * Amplitude endpoint URL. - */ - 'endpoint': string; - /** - * API key for GoodData common project - used by backend. - */ - 'gdCommonApiKey': string; - /** - * Optional reporting endpoint for proxying telemetry events. - */ - 'reportingEndpoint'?: string; -} - /** * List of users who created catalog objects in the workspace hierarchy. */ @@ -19203,7 +19181,6 @@ export interface TelemetryContext { * Available telemetry services. */ export interface TelemetryServices { - 'amplitude'?: AmplitudeService; 'matomo'?: MatomoService; 'openTelemetry'?: OpenTelemetryService; } diff --git a/libs/api-client-tiger/src/generated/metadata-json-api/openapi-spec.json b/libs/api-client-tiger/src/generated/metadata-json-api/openapi-spec.json index 58475c03de5..0dc3063a03e 100644 --- a/libs/api-client-tiger/src/generated/metadata-json-api/openapi-spec.json +++ b/libs/api-client-tiger/src/generated/metadata-json-api/openapi-spec.json @@ -32694,33 +32694,6 @@ }, "description": "An all-time date filter that does not restrict by date range. Controls how rows with empty (null/missing) date values are handled." }, - "AmplitudeService": { - "required": [ - "aiProjectApiKey", - "endpoint", - "gdCommonApiKey" - ], - "type": "object", - "properties": { - "aiProjectApiKey": { - "type": "string", - "description": "API key for AI project - intended for frontend use." - }, - "endpoint": { - "type": "string", - "description": "Amplitude endpoint URL." - }, - "gdCommonApiKey": { - "type": "string", - "description": "API key for GoodData common project - used by backend." - }, - "reportingEndpoint": { - "type": "string", - "description": "Optional reporting endpoint for proxying telemetry events." - } - }, - "description": "Amplitude service." - }, "AnalyticsCatalogCreatedBy": { "required": [ "reasoning", @@ -71717,9 +71690,6 @@ "TelemetryServices": { "type": "object", "properties": { - "amplitude": { - "$ref": "#/components/schemas/AmplitudeService" - }, "matomo": { "$ref": "#/components/schemas/MatomoService" }, diff --git a/libs/sdk-backend-base/package.json b/libs/sdk-backend-base/package.json index eae0c3bf032..429ec59c84c 100644 --- a/libs/sdk-backend-base/package.json +++ b/libs/sdk-backend-base/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-backend-base", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData.UI SDK - Base for backend implementations", "license": "MIT", "author": "GoodData", diff --git a/libs/sdk-backend-mockingbird/package.json b/libs/sdk-backend-mockingbird/package.json index 73c93c7e82b..93f829cc41f 100644 --- a/libs/sdk-backend-mockingbird/package.json +++ b/libs/sdk-backend-mockingbird/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-backend-mockingbird", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "Mock GoodData Backend SPI implementation", "license": "MIT", "author": "GoodData", diff --git a/libs/sdk-backend-spi/package.json b/libs/sdk-backend-spi/package.json index fbcffa39e69..ee843fbf8ae 100644 --- a/libs/sdk-backend-spi/package.json +++ b/libs/sdk-backend-spi/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-backend-spi", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData Backend SPI abstraction interfaces", "license": "MIT", "author": "GoodData", diff --git a/libs/sdk-backend-tiger/package.json b/libs/sdk-backend-tiger/package.json index 01aecd0e8b5..3c9a045ab0c 100644 --- a/libs/sdk-backend-tiger/package.json +++ b/libs/sdk-backend-tiger/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-backend-tiger", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData Backend SPI implementation for GoodData Cloud and GoodData.CN", "license": "MIT", "author": "GoodData", diff --git a/libs/sdk-backend-tiger/src/convertors/fromBackend/analyticalDashboards/v2/AnalyticalDashboardConverter.ts b/libs/sdk-backend-tiger/src/convertors/fromBackend/analyticalDashboards/v2/AnalyticalDashboardConverter.ts index 24f5ae17ee2..0d376eaed24 100644 --- a/libs/sdk-backend-tiger/src/convertors/fromBackend/analyticalDashboards/v2/AnalyticalDashboardConverter.ts +++ b/libs/sdk-backend-tiger/src/convertors/fromBackend/analyticalDashboards/v2/AnalyticalDashboardConverter.ts @@ -132,7 +132,7 @@ interface IAnalyticalDashboardContent { disableUserFilterReset?: boolean; disableUserFilterSave?: boolean; disableFilterViews?: boolean; - disablePersistentFiltersAcrossTabs?: boolean; + enablePersistentFiltersAcrossTabs?: boolean; evaluationFrequency?: string; sectionHeadersDateDataSet?: IdentifierRef; timezoneConfig?: IDashboardTimezoneConfig; @@ -225,6 +225,11 @@ function getConvertedAnalyticalDashboardContent( filterContextsList: IFilterContext[], filterContextOverride?: IFilterContext, ): IAnalyticalDashboardContent { + const legacyDisablePersistentFiltersAcrossTabs = ( + analyticalDashboard as AnalyticalDashboardModelV2.IAnalyticalDashboard & { + disablePersistentFiltersAcrossTabs?: boolean; + } + ).disablePersistentFiltersAcrossTabs; const tabs = analyticalDashboard.tabs?.map((tab) => { return convertDashboardTab(tab, filterContextsList, filterContextOverride); }); @@ -270,7 +275,11 @@ function getConvertedAnalyticalDashboardContent( disableUserFilterReset: analyticalDashboard.disableUserFilterReset, disableUserFilterSave: analyticalDashboard.disableUserFilterSave, disableFilterViews: analyticalDashboard.disableFilterViews, - disablePersistentFiltersAcrossTabs: analyticalDashboard.disablePersistentFiltersAcrossTabs, + enablePersistentFiltersAcrossTabs: + analyticalDashboard.enablePersistentFiltersAcrossTabs ?? + (typeof legacyDisablePersistentFiltersAcrossTabs === "boolean" + ? !legacyDisablePersistentFiltersAcrossTabs + : undefined), evaluationFrequency: analyticalDashboard.evaluationFrequency, sectionHeadersDateDataSet: cloneWithSanitizedIds(analyticalDashboard.sectionHeadersDateDataSet), timezoneConfig: analyticalDashboard.timezoneConfig, @@ -326,7 +335,7 @@ export function convertDashboard( disableUserFilterReset, disableUserFilterSave, disableFilterViews, - disablePersistentFiltersAcrossTabs, + enablePersistentFiltersAcrossTabs, evaluationFrequency, sectionHeadersDateDataSet, timezoneConfig, @@ -368,7 +377,7 @@ export function convertDashboard( disableUserFilterReset, disableUserFilterSave, disableFilterViews, - disablePersistentFiltersAcrossTabs, + enablePersistentFiltersAcrossTabs, evaluationFrequency, sectionHeadersDateDataSet, timezoneConfig, diff --git a/libs/sdk-backend-tiger/src/convertors/toBackend/AnalyticalDashboardConverter.ts b/libs/sdk-backend-tiger/src/convertors/toBackend/AnalyticalDashboardConverter.ts index c66004b89dd..b1fa81bae4e 100644 --- a/libs/sdk-backend-tiger/src/convertors/toBackend/AnalyticalDashboardConverter.ts +++ b/libs/sdk-backend-tiger/src/convertors/toBackend/AnalyticalDashboardConverter.ts @@ -149,7 +149,7 @@ function convertDashboardTabToBackend( * @param useWidgetLocalIdentifiers - Whether to preserve widget local identifiers * @param enableDashboardSectionHeadersDateDataSet - Whether to include section headers date data set * @param enableAnalyticalDashboardVersion3 - When true and the dashboard has tabs, produce a V3 analytical - * @param enableDashboardPersistentFiltersAcrossTabs - Whether to include disable persistent filters across tabs + * @param enableDashboardPersistentFiltersAcrossTabs - Whether to include persistent filters across tabs * dashboard: tabs-only, no root-level layout / filter configs / parameters. V3 cannot be read * by SDK versions that only know V2 root-level properties. Opt-in. * @returns Tiger analytical dashboard (V2 by default, V3 when the flag is on and tabs are present). @@ -196,7 +196,7 @@ export function convertAnalyticalDashboard( disableUserFilterSave: dashboard.disableUserFilterSave, disableFilterViews: dashboard.disableFilterViews, ...(enableDashboardPersistentFiltersAcrossTabs - ? { disablePersistentFiltersAcrossTabs: dashboard.disablePersistentFiltersAcrossTabs } + ? { enablePersistentFiltersAcrossTabs: dashboard.enablePersistentFiltersAcrossTabs } : {}), evaluationFrequency: dashboard.evaluationFrequency, timezoneConfig: dashboard.timezoneConfig, @@ -258,7 +258,7 @@ export function convertAnalyticalDashboard( disableUserFilterSave: dashboard.disableUserFilterSave, disableFilterViews: dashboard.disableFilterViews, ...(enableDashboardPersistentFiltersAcrossTabs - ? { disablePersistentFiltersAcrossTabs: dashboard.disablePersistentFiltersAcrossTabs } + ? { enablePersistentFiltersAcrossTabs: dashboard.enablePersistentFiltersAcrossTabs } : {}), evaluationFrequency: dashboard.evaluationFrequency, timezoneConfig: dashboard.timezoneConfig, diff --git a/libs/sdk-backend-tiger/src/convertors/toBackend/afm/AacFilterParity.test.ts b/libs/sdk-backend-tiger/src/convertors/toBackend/afm/AacFilterParity.test.ts new file mode 100644 index 00000000000..2556d05bcdb --- /dev/null +++ b/libs/sdk-backend-tiger/src/convertors/toBackend/afm/AacFilterParity.test.ts @@ -0,0 +1,74 @@ +// (C) 2026 GoodData Corporation + +import { describe, expect, it } from "vitest"; + +import { buildAfmExecution } from "@gooddata/sdk-code-convertors"; +import { + type EmptyValues, + type IFilter, + newAbsoluteDateFilter, + newAllTimeFilter, + newRelativeDateFilter, +} from "@gooddata/sdk-model"; + +import { convertFilter } from "./FilterConverter.js"; + +/** + * `buildAfmExecution` (sdk-code-convertors, AaC query preview) is a second implementation of the model + * filter to AFM conversion done by `convertFilter`. The two must agree on the filters AaC can express, or + * the preview shows different data than the stored insight. Keep this table in sync when a filter + * property is added to either side. + * + * Deliberately not compared: `absoluteGranularityDateFilter` (not expressible in AaC yet). + */ +describe("AaC query preview AFM matches the model filter conversion", () => { + const DATASET = "expected_ship_date"; + const MODES: EmptyValues[] = ["include", "exclude", "only"]; + + const Scenarios: Array<[string, unknown, IFilter]> = [ + ["All time, no mode", { type: "date_filter", using: DATASET }, newAllTimeFilter(DATASET)], + ...MODES.map((mode): [string, unknown, IFilter] => [ + `All time, ${mode}`, + { type: "date_filter", using: DATASET, empty_values: mode }, + newAllTimeFilter(DATASET, undefined, mode), + ]), + ...MODES.map((mode): [string, unknown, IFilter] => [ + `relative, ${mode}`, + { + type: "date_filter", + using: DATASET, + granularity: "YEAR", + from: -10, + to: 0, + empty_values: mode, + }, + newRelativeDateFilter(DATASET, "GDC.time.year", -10, 0, undefined, undefined, mode), + ]), + ...MODES.map((mode): [string, unknown, IFilter] => [ + `absolute, ${mode}`, + { type: "date_filter", using: DATASET, from: "2020-01-01", to: "2020-12-31", empty_values: mode }, + newAbsoluteDateFilter(DATASET, "2020-01-01", "2020-12-31", undefined, mode), + ]), + [ + "relative, no mode", + { type: "date_filter", using: DATASET, granularity: "YEAR", from: -10, to: 0 }, + newRelativeDateFilter(DATASET, "GDC.time.year", -10, 0), + ], + [ + "absolute, no mode", + { type: "date_filter", using: DATASET, from: "2020-01-01", to: "2020-12-31" }, + newAbsoluteDateFilter(DATASET, "2020-01-01", "2020-12-31"), + ], + ]; + + it.each(Scenarios)("%s", (_name, yamlFilter, modelFilter) => { + const query = { fields: {}, filter_by: { f: yamlFilter } } as unknown as Parameters< + typeof buildAfmExecution + >[1]; + + const { filters } = buildAfmExecution([], query).execution.execution; + + // convertFilter returns null for a filter it does not send + expect(filters).toEqual([convertFilter(modelFilter)].filter(Boolean)); + }); +}); diff --git a/libs/sdk-backend-tiger/tests/goodmock/recordings/mappings.json b/libs/sdk-backend-tiger/tests/goodmock/recordings/mappings.json index 9f9d19d683f..f3364bbb3c2 100644 --- a/libs/sdk-backend-tiger/tests/goodmock/recordings/mappings.json +++ b/libs/sdk-backend-tiger/tests/goodmock/recordings/mappings.json @@ -17848,12 +17848,6 @@ "userHash": "1068484ba1a28ec9c53a612c58aef9f6c73eade0" }, "services": { - "amplitude": { - "aiProjectApiKey": "f2db721bd53a2b2bd4d3b4c653f368ab", - "endpoint": "https://api2.amplitude.com/2/httpapi", - "gdCommonApiKey": "d5bfd1166bfb74cca8adf0f8a9656751", - "reportingEndpoint": "/tlm/amp" - }, "matomo": { "host": "https://matomo.anywhere.gooddata.com/matomo.php", "reportingEndpoint": "/tlm/mat", diff --git a/libs/sdk-code-convertors/api/sdk-code-convertors.api.md b/libs/sdk-code-convertors/api/sdk-code-convertors.api.md index db482904d95..e19981e3335 100644 --- a/libs/sdk-code-convertors/api/sdk-code-convertors.api.md +++ b/libs/sdk-code-convertors/api/sdk-code-convertors.api.md @@ -1371,7 +1371,7 @@ export interface CustomTooltip { export type CustomTooltipPlacement = "above" | "below" | "replace"; // @internal (undocumented) -export type DashboardDefinition = Pick & { +export type DashboardDefinition = Pick & { version: string; }; diff --git a/libs/sdk-code-convertors/package.json b/libs/sdk-code-convertors/package.json index c871ce8b410..705baadafff 100644 --- a/libs/sdk-code-convertors/package.json +++ b/libs/sdk-code-convertors/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-code-convertors", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData AAC declarative converters", "license": "MIT", "author": "GoodData", diff --git a/libs/sdk-code-convertors/python/pyproject.toml b/libs/sdk-code-convertors/python/pyproject.toml index 3d8a24cacb1..8a994cda0ce 100644 --- a/libs/sdk-code-convertors/python/pyproject.toml +++ b/libs/sdk-code-convertors/python/pyproject.toml @@ -2,7 +2,7 @@ [project] name = "gooddata-code-convertors" -version = "11.66.0a0" +version = "11.67.0a1" description = "GoodData AAC YAML / Declarative API code converters (WASM-powered)" readme = "README.md" license = { file = "LICENSE" } diff --git a/libs/sdk-code-convertors/python/src/gooddata_code_convertors/_types.py b/libs/sdk-code-convertors/python/src/gooddata_code_convertors/_types.py index 4d4206ff01f..5a23f55d2c0 100644 --- a/libs/sdk-code-convertors/python/src/gooddata_code_convertors/_types.py +++ b/libs/sdk-code-convertors/python/src/gooddata_code_convertors/_types.py @@ -1,5 +1,5 @@ # (C) 2026 GoodData Corporation -# schema-hash: 582442dbcf1e97fa695a65710a31333730a92ec74b68760d83bcb1180004fd0c +# schema-hash: acb0fdf12721d2d96de458b3c85af98942b972e7ca31a3621402f9565fd2b013 from __future__ import annotations @@ -1428,7 +1428,7 @@ class QueryField11(TypedDict): operator: NotRequired[ Literal['SUM', 'DIFFERENCE', 'MULTIPLICATION', 'RATIO', 'CHANGE'] ] - type: Literal['PREVIOUS_YEAR', 'PREVIOUS_PERIOD'] + type: Literal['PREVIOUS_YEAR'] date_filter: str title: NotRequired[Title] @@ -1442,7 +1442,7 @@ class QueryField13(TypedDict): operator: NotRequired[ Literal['SUM', 'DIFFERENCE', 'MULTIPLICATION', 'RATIO', 'CHANGE'] ] - type: Literal['PREVIOUS_YEAR', 'PREVIOUS_PERIOD'] + type: Literal['PREVIOUS_PERIOD'] date_filter: str title: NotRequired[Title] period: NotRequired[float] @@ -2202,17 +2202,11 @@ class QueryField5(TypedDict): | QueryField1 | QueryField2 | QueryField3 - | QueryField2 | QueryField5 - | QueryField2 | QueryField7 - | QueryField2 | QueryField9 - | QueryField2 | QueryField11 - | QueryField2 | QueryField13 - | QueryField2 ) diff --git a/libs/sdk-code-convertors/python/src/gooddata_code_convertors/pydantic_models.py b/libs/sdk-code-convertors/python/src/gooddata_code_convertors/pydantic_models.py index 9a32c55d6dd..e7647b9fcad 100644 --- a/libs/sdk-code-convertors/python/src/gooddata_code_convertors/pydantic_models.py +++ b/libs/sdk-code-convertors/python/src/gooddata_code_convertors/pydantic_models.py @@ -1,5 +1,5 @@ # (C) 2026 GoodData Corporation -# schema-hash: 582442dbcf1e97fa695a65710a31333730a92ec74b68760d83bcb1180004fd0c +# schema-hash: acb0fdf12721d2d96de458b3c85af98942b972e7ca31a3621402f9565fd2b013 from __future__ import annotations @@ -2132,8 +2132,6 @@ class Operator10(Enum): class Type54(Enum): PREVIOUS_YEAR = 'PREVIOUS_YEAR' - PREVIOUS_PERIOD = 'PREVIOUS_PERIOD' - PREVIOUS_YEAR_1 = 'PREVIOUS_YEAR' class Aggregation11(Enum): @@ -2213,9 +2211,7 @@ class Operator12(Enum): class Type56(Enum): - PREVIOUS_YEAR = 'PREVIOUS_YEAR' PREVIOUS_PERIOD = 'PREVIOUS_PERIOD' - PREVIOUS_PERIOD_1 = 'PREVIOUS_PERIOD' class Aggregation13(Enum): @@ -7145,7 +7141,7 @@ class Dashboard1(BaseModel): ) persistent_filters_across_tabs: bool | None = Field( None, - description='Whether persistent filters across tabs are enabled for this dashboard. Defaults to true.', + description='Whether persistent filters across tabs are enabled for this dashboard. When omitted, the setting is off.', ) timezone_config: TimezoneConfig | None = Field( None, @@ -7471,7 +7467,7 @@ class Dashboard(BaseModel): ) persistent_filters_across_tabs: bool | None = Field( None, - description='Whether persistent filters across tabs are enabled for this dashboard. Defaults to true.', + description='Whether persistent filters across tabs are enabled for this dashboard. When omitted, the setting is off.', ) timezone_config: TimezoneConfig | None = Field( None, diff --git a/libs/sdk-code-convertors/src/buildAfmExecution.test.ts b/libs/sdk-code-convertors/src/buildAfmExecution.test.ts index 9e2d052caa6..c6b52e8d24d 100644 --- a/libs/sdk-code-convertors/src/buildAfmExecution.test.ts +++ b/libs/sdk-code-convertors/src/buildAfmExecution.test.ts @@ -264,3 +264,87 @@ describe("buildAfmExecution dimensions", () => { ]); }); }); + +describe("buildAfmExecution date filter empty values", () => { + const dataset = { identifier: { id: "expected_ship_date", type: "dataset" } }; + const filtersOf = (filter_by: unknown) => + buildAfmExecution(emptyEntities, buildQueryWithFilters(filter_by)).execution.execution.filters; + + it("emits allTimeDateFilter carrying emptyValueHandling for All time", () => { + expect( + filtersOf({ f: { type: "date_filter", using: "expected_ship_date", empty_values: "only" } }), + ).toEqual([{ allTimeDateFilter: { dataset, emptyValueHandling: "ONLY" } }]); + }); + + it.each([undefined, "include"])("sends no filter for a no-op All time (empty_values: %s)", (mode) => { + expect( + filtersOf({ f: { type: "date_filter", using: "expected_ship_date", empty_values: mode } }), + ).toEqual([]); + }); + + it("maps empty_values on a relative date filter", () => { + expect( + filtersOf({ + f: { + type: "date_filter", + using: "expected_ship_date", + granularity: "YEAR", + from: -10, + to: 0, + empty_values: "include", + }, + }), + ).toEqual([ + { + relativeDateFilter: { + dataset, + granularity: "YEAR", + from: -10, + to: 0, + emptyValueHandling: "INCLUDE", + }, + }, + ]); + }); + + it("maps empty_values on an absolute date filter", () => { + expect( + filtersOf({ + f: { + type: "date_filter", + using: "expected_ship_date", + from: "2020-01-01", + to: "2020-12-31", + empty_values: "exclude", + }, + }), + ).toEqual([ + { + absoluteDateFilter: { + dataset, + from: "2020-01-01", + to: "2020-12-31", + emptyValueHandling: "EXCLUDE", + }, + }, + ]); + }); + + it("applies empty_values to metric-level filter_by", () => { + const query = { + fields: { + m1: { + aggregation: "COUNT", + using: "attribute/order_item_id", + filter_by: { + f: { type: "date_filter", using: "expected_ship_date", empty_values: "only" }, + }, + }, + }, + } as unknown as Query; + const { measures } = buildAfmExecution(emptyEntities, query).execution.execution; + expect((measures[0].definition as any).measure.filters).toEqual([ + { allTimeDateFilter: { dataset, emptyValueHandling: "ONLY" } }, + ]); + }); +}); diff --git a/libs/sdk-code-convertors/src/dashboardConversion.test.ts b/libs/sdk-code-convertors/src/dashboardConversion.test.ts index 3be838a5df1..413abb48167 100644 --- a/libs/sdk-code-convertors/src/dashboardConversion.test.ts +++ b/libs/sdk-code-convertors/src/dashboardConversion.test.ts @@ -360,7 +360,7 @@ describe("dashboard conversion", () => { expect(content.disableUserFilterSave).toBe(true); expect(content.disableUserFilterReset).toBe(true); expect(content.disableFilterViews).toBe(true); - expect(content.disablePersistentFiltersAcrossTabs).toBe(true); + expect(content.enablePersistentFiltersAcrossTabs).toBe(false); }); it("should convert dashboard timezone config as one content object", () => { @@ -612,6 +612,33 @@ describe("dashboard conversion", () => { expect(json.cross_filtering).toBe(false); }); + it.each([true, false])("should round-trip persistent_filters_across_tabs=%s", (value) => { + const input = makeDashboard({ persistent_filters_across_tabs: value }); + + const { dashboard, filterContext } = yamlDashboardToDeclarative(emptyEntities, input); + const { json } = declarativeDashboardToYaml( + emptyFromEntities, + dashboard, + filterContext ? [filterContext] : [], + ); + + expect(json.persistent_filters_across_tabs).toBe(value); + }); + + it("should not emit persistent_filters_across_tabs for a stored null", () => { + const { dashboard, filterContext } = yamlDashboardToDeclarative(emptyEntities, makeDashboard({})); + dashboard.content = { ...dashboard.content, enablePersistentFiltersAcrossTabs: null } as any; + + const { json, content } = declarativeDashboardToYaml( + emptyFromEntities, + dashboard, + filterContext ? [filterContext] : [], + ); + + expect(json).not.toHaveProperty("persistent_filters_across_tabs"); + expect(content).not.toContain("persistent_filters_across_tabs"); + }); + it("should round-trip dashboard timezone config", () => { const input = makeDashboard({ timezone_config: { diff --git a/libs/sdk-code-convertors/src/execution/buildAfmExecution.ts b/libs/sdk-code-convertors/src/execution/buildAfmExecution.ts index 2ed15496a2f..977becafb4e 100644 --- a/libs/sdk-code-convertors/src/execution/buildAfmExecution.ts +++ b/libs/sdk-code-convertors/src/execution/buildAfmExecution.ts @@ -16,6 +16,7 @@ import { } from "@gooddata/api-client-tiger"; import type { ComparisonCondition, + DateFilter, Query, QueryFilters, RangeCondition, @@ -30,6 +31,7 @@ import { parseDateValues, yamlConditionToMatch } from "../utils/filterUtils.js"; import { getFullField } from "../utils/sharedUtils.js"; import { isAbsoluteDateFilter, + isAllTimeDateFilter, isArbitraryTextFilter, isArithmeticMetricField, isAttributeField, @@ -254,12 +256,30 @@ function buildDimensions( }; } +/** + * `empty_values` is the date filter's own mode; the AFM spells it in upper case. + */ +function dateFilterEmptyValueHandling(filter: DateFilter): { + emptyValueHandling?: "INCLUDE" | "EXCLUDE" | "ONLY"; +} { + const { empty_values } = filter; + return empty_values === "include" || empty_values === "exclude" || empty_values === "only" + ? { emptyValueHandling: empty_values.toUpperCase() as "INCLUDE" | "EXCLUDE" | "ONLY" } + : {}; +} + +/** + * Converts AaC `filter_by` into AFM filters for the query preview. + * + * sdk-backend-tiger's `convertFilter` converts the equivalent model filters for a stored insight. When + * you add or change a date filter property here, change it there too, and extend + * `AacFilterParity.test.ts` in sdk-backend-tiger, which fails if the two diverge. + */ function buildFilters(entities: ExportEntities, filters_by: QueryFilters | undefined): FilterDefinition[] { if (!filters_by) { return []; } - // TODO const filters: FilterDefinition[] = []; Object.keys(filters_by ?? {}).forEach((key) => { @@ -270,10 +290,23 @@ function buildFilters(entities: ExportEntities, filters_by: QueryFilters | undef dataset: createIdentifier(filter.using, { forceType: "dataset" }), from: filter.from as string, to: filter.to as string, + ...dateFilterEmptyValueHandling(filter), }, }); } - if (isRelativeDateFilter(filter)) { + if (isAllTimeDateFilter(filter)) { + // Mirrors convertFilter in sdk-backend-tiger: All time only has an effect with an empty + // values mode that filters, anything else is a no-op and is not sent. The relative filter + // is not an option, it requires from/to/granularity. + if (filter.empty_values === "exclude" || filter.empty_values === "only") { + filters.push({ + allTimeDateFilter: { + dataset: createIdentifier(filter.using, { forceType: "dataset" }), + ...dateFilterEmptyValueHandling(filter), + }, + }); + } + } else if (isRelativeDateFilter(filter)) { filters.push({ relativeDateFilter: { dataset: createIdentifier(filter.using, { forceType: "dataset" }), @@ -281,6 +314,7 @@ function buildFilters(entities: ExportEntities, filters_by: QueryFilters | undef undefined) as RelativeDateFilterRelativeDateFilterGranularityEnum, from: filter.from as number, to: filter.to as number, + ...dateFilterEmptyValueHandling(filter), }, }); } diff --git a/libs/sdk-code-convertors/src/from/declarativeDashboardToYaml.ts b/libs/sdk-code-convertors/src/from/declarativeDashboardToYaml.ts index 1cd134d6cf9..33b6a1e9209 100644 --- a/libs/sdk-code-convertors/src/from/declarativeDashboardToYaml.ts +++ b/libs/sdk-code-convertors/src/from/declarativeDashboardToYaml.ts @@ -137,8 +137,8 @@ export function declarativeDashboardToYaml( doc.add(entryWithSpace("filter_views", false)); } - if (content.disablePersistentFiltersAcrossTabs) { - doc.add(entryWithSpace("persistent_filters_across_tabs", false)); + if (typeof content.enablePersistentFiltersAcrossTabs === "boolean") { + doc.add(entryWithSpace("persistent_filters_across_tabs", content.enablePersistentFiltersAcrossTabs)); } if (content.timezoneConfig) { doc.add(entryWithSpace("timezone_config", declarativeTimezoneConfigToYaml(content.timezoneConfig))); diff --git a/libs/sdk-code-convertors/src/to/yamlDashboardToDeclarative.ts b/libs/sdk-code-convertors/src/to/yamlDashboardToDeclarative.ts index 4789b79bbbd..f4eed3ad298 100644 --- a/libs/sdk-code-convertors/src/to/yamlDashboardToDeclarative.ts +++ b/libs/sdk-code-convertors/src/to/yamlDashboardToDeclarative.ts @@ -105,7 +105,7 @@ export type DashboardDefinition = Pick< | "disableUserFilterSave" | "disableUserFilterReset" | "disableFilterViews" - | "disablePersistentFiltersAcrossTabs" + | "enablePersistentFiltersAcrossTabs" | "attributeFilterConfigs" | "measureValueFilterConfigs" | "filterContextRef" @@ -370,8 +370,8 @@ export function yamlDashboardToDeclarative( ...(input.user_filters_save === false ? { disableUserFilterSave: true } : {}), ...(input.user_filters_reset === false ? { disableUserFilterReset: true } : {}), ...(input.filter_views === false ? { disableFilterViews: true } : {}), - ...(input.persistent_filters_across_tabs === false - ? { disablePersistentFiltersAcrossTabs: true } + ...(typeof input.persistent_filters_across_tabs === "boolean" + ? { enablePersistentFiltersAcrossTabs: input.persistent_filters_across_tabs } : {}), } : { @@ -388,8 +388,8 @@ export function yamlDashboardToDeclarative( ...(input.user_filters_save === false ? { disableUserFilterSave: true } : {}), ...(input.user_filters_reset === false ? { disableUserFilterReset: true } : {}), ...(input.filter_views === false ? { disableFilterViews: true } : {}), - ...(input.persistent_filters_across_tabs === false - ? { disablePersistentFiltersAcrossTabs: true } + ...(typeof input.persistent_filters_across_tabs === "boolean" + ? { enablePersistentFiltersAcrossTabs: input.persistent_filters_across_tabs } : {}), filterContextRef: filterContext ? createIdentifier(filterContext.id, { forceType: "filterContext" }) diff --git a/libs/sdk-code-schemas/package.json b/libs/sdk-code-schemas/package.json index dfdf32334e4..ea99146225e 100644 --- a/libs/sdk-code-schemas/package.json +++ b/libs/sdk-code-schemas/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-code-schemas", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData AAC JSON Schema types and compiled schemas", "license": "MIT", "author": "GoodData", diff --git a/libs/sdk-code-schemas/schemas/v1/src/dashboard.json b/libs/sdk-code-schemas/schemas/v1/src/dashboard.json index 700620d064f..9b8ac81d377 100644 --- a/libs/sdk-code-schemas/schemas/v1/src/dashboard.json +++ b/libs/sdk-code-schemas/schemas/v1/src/dashboard.json @@ -85,7 +85,7 @@ }, "persistent_filters_across_tabs": { "type": "boolean", - "description": "Whether persistent filters across tabs are enabled for this dashboard. Defaults to true." + "description": "Whether persistent filters across tabs are enabled for this dashboard. When omitted, the setting is off." }, "timezone_config": { "$ref": "#/$defs/timezoneConfig", diff --git a/libs/sdk-code-schemas/src/v1/metadata.json b/libs/sdk-code-schemas/src/v1/metadata.json index d29cfad52a9..17579d2453c 100644 --- a/libs/sdk-code-schemas/src/v1/metadata.json +++ b/libs/sdk-code-schemas/src/v1/metadata.json @@ -610,7 +610,7 @@ }, "persistent_filters_across_tabs": { "type": "boolean", - "description": "Whether persistent filters across tabs are enabled for this dashboard. Defaults to true." + "description": "Whether persistent filters across tabs are enabled for this dashboard. When omitted, the setting is off." }, "timezone_config": { "$ref": "#/$defs/timezoneConfig", @@ -6688,7 +6688,7 @@ }, "persistent_filters_across_tabs": { "type": "boolean", - "description": "Whether persistent filters across tabs are enabled for this dashboard. Defaults to true." + "description": "Whether persistent filters across tabs are enabled for this dashboard. When omitted, the setting is off." }, "timezone_config": { "$ref": "#/$defs/timezoneConfig", diff --git a/libs/sdk-code-schemas/src/v1/metadata.ts b/libs/sdk-code-schemas/src/v1/metadata.ts index 78e554ad24e..fd4494f4b76 100644 --- a/libs/sdk-code-schemas/src/v1/metadata.ts +++ b/libs/sdk-code-schemas/src/v1/metadata.ts @@ -2510,7 +2510,7 @@ export interface Dashboard { */ filter_views?: boolean; /** - * Whether persistent filters across tabs are enabled for this dashboard. Defaults to true. + * Whether persistent filters across tabs are enabled for this dashboard. When omitted, the setting is off. */ persistent_filters_across_tabs?: boolean; timezone_config?: TimezoneConfig; diff --git a/libs/sdk-e2e-utils/package.json b/libs/sdk-e2e-utils/package.json index 9311e73a048..2f8fca7a2f9 100644 --- a/libs/sdk-e2e-utils/package.json +++ b/libs/sdk-e2e-utils/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-e2e-utils", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData utility functions for Playwright E2E tests", "license": "MIT", "author": "GoodData", diff --git a/libs/sdk-embedding/package.json b/libs/sdk-embedding/package.json index 0318e65a72e..03ce3d927df 100644 --- a/libs/sdk-embedding/package.json +++ b/libs/sdk-embedding/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-embedding", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData Embedding APIs", "license": "MIT", "author": "GoodData", diff --git a/libs/sdk-maql-language-server/package.json b/libs/sdk-maql-language-server/package.json index 95334e32d51..21ba62e631e 100644 --- a/libs/sdk-maql-language-server/package.json +++ b/libs/sdk-maql-language-server/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-maql-language-server", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "license": "MIT", "author": { "name": "Stellar", diff --git a/libs/sdk-model/api/sdk-model.api.md b/libs/sdk-model/api/sdk-model.api.md index f411417de85..d6b6f9732cd 100644 --- a/libs/sdk-model/api/sdk-model.api.md +++ b/libs/sdk-model/api/sdk-model.api.md @@ -2318,9 +2318,9 @@ export interface IDashboard extends IDashboardBase, readonly dateFilterConfigs?: IDashboardDateFilterConfigItem[]; readonly disableCrossFiltering?: boolean; readonly disableFilterViews?: boolean; - readonly disablePersistentFiltersAcrossTabs?: boolean; readonly disableUserFilterReset?: boolean; readonly disableUserFilterSave?: boolean; + readonly enablePersistentFiltersAcrossTabs?: boolean; readonly evaluationFrequency?: string; readonly filterContext?: IFilterContext | ITempFilterContext; readonly layout?: IDashboardLayout; @@ -2462,9 +2462,9 @@ export interface IDashboardDefinition extends IDashb readonly dateFilterConfigs?: IDashboardDateFilterConfigItem[]; readonly disableCrossFiltering?: boolean; readonly disableFilterViews?: boolean; - readonly disablePersistentFiltersAcrossTabs?: boolean; readonly disableUserFilterReset?: boolean; readonly disableUserFilterSave?: boolean; + readonly enablePersistentFiltersAcrossTabs?: boolean; readonly evaluationFrequency?: string; readonly filterContext?: IFilterContext | IFilterContextDefinition; readonly layout?: IDashboardLayout; diff --git a/libs/sdk-model/package.json b/libs/sdk-model/package.json index 7f0752da617..959770c3485 100644 --- a/libs/sdk-model/package.json +++ b/libs/sdk-model/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-model", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData Model definitions used by UI components and Backend SPI", "license": "MIT", "author": "GoodData", diff --git a/libs/sdk-model/src/dashboard/dashboard.ts b/libs/sdk-model/src/dashboard/dashboard.ts index 82103fd87d7..0796ae6a238 100644 --- a/libs/sdk-model/src/dashboard/dashboard.ts +++ b/libs/sdk-model/src/dashboard/dashboard.ts @@ -693,10 +693,15 @@ export interface IDashboard readonly disableFilterViews?: boolean; /** - * Disables persistent filters across tabs for this dashboard. + * Enables persistent filters across tabs for this dashboard. + * + * @remarks + * When omitted, the feature stays off. Dashboards saved before this setting + * existed keep a separate filter selection on each tab. + * * @alpha */ - readonly disablePersistentFiltersAcrossTabs?: boolean; + readonly enablePersistentFiltersAcrossTabs?: boolean; /** * Data sets related to the dashboard, as defined by the includes directive @@ -803,9 +808,13 @@ export interface IDashboardDefinition readonly disableFilterViews?: boolean; /** - * Disables persistent filters across tabs for this dashboard. + * Enables persistent filters across tabs for this dashboard. + * + * @remarks + * When omitted, the feature stays off. Dashboards saved before this setting + * existed keep a separate filter selection on each tab. */ - readonly disablePersistentFiltersAcrossTabs?: boolean; + readonly enablePersistentFiltersAcrossTabs?: boolean; /** * Evaluation frequency of alerts for the dashboard. diff --git a/libs/sdk-pluggable-application-model/package.json b/libs/sdk-pluggable-application-model/package.json index a2661ec0dc9..c6d55863327 100644 --- a/libs/sdk-pluggable-application-model/package.json +++ b/libs/sdk-pluggable-application-model/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-pluggable-application-model", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData SDK model contracts for pluggable applications", "license": "MIT", "author": "GoodData Corporation", diff --git a/libs/sdk-ui-all/package.json b/libs/sdk-ui-all/package.json index b6ffc6deb1e..cfe68f7c12b 100644 --- a/libs/sdk-ui-all/package.json +++ b/libs/sdk-ui-all/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-ui-all", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData SDK - All-In-One", "license": "LicenseRef-LICENSE", "author": "GoodData", diff --git a/libs/sdk-ui-analytical-designer/package.json b/libs/sdk-ui-analytical-designer/package.json index 1c12ad6f4be..94cb78e20bc 100644 --- a/libs/sdk-ui-analytical-designer/package.json +++ b/libs/sdk-ui-analytical-designer/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-ui-analytical-designer", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "Analytical Designer UI library — core components, sagas, reducers, and application logic", "license": "LicenseRef-LICENSE", "author": { diff --git a/libs/sdk-ui-application-header/package.json b/libs/sdk-ui-application-header/package.json index bff53733c41..8029e485d30 100644 --- a/libs/sdk-ui-application-header/package.json +++ b/libs/sdk-ui-application-header/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-ui-application-header", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData SDK application header components — workspace picker, notifications dropdown", "license": "MIT", "author": "GoodData Corporation", diff --git a/libs/sdk-ui-catalog/package.json b/libs/sdk-ui-catalog/package.json index 1d61622e1de..6dc2d648095 100644 --- a/libs/sdk-ui-catalog/package.json +++ b/libs/sdk-ui-catalog/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-ui-catalog", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData SDK - Analytics Catalog", "license": "MIT", "author": "GoodData Corporation", diff --git a/libs/sdk-ui-charts/package.json b/libs/sdk-ui-charts/package.json index 3962f76b805..383236eef58 100644 --- a/libs/sdk-ui-charts/package.json +++ b/libs/sdk-ui-charts/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-ui-charts", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData.UI SDK - Charts", "license": "LicenseRef-LICENSE", "author": "GoodData Corporation", diff --git a/libs/sdk-ui-charts/src/highcharts/ChartTransformation.test.tsx b/libs/sdk-ui-charts/src/highcharts/ChartTransformation.test.tsx index 6d2d293bb57..9fc32841aaf 100644 --- a/libs/sdk-ui-charts/src/highcharts/ChartTransformation.test.tsx +++ b/libs/sdk-ui-charts/src/highcharts/ChartTransformation.test.tsx @@ -212,6 +212,50 @@ describe("ChartTransformation", () => { }); describe("onDataTooLarge", () => { + it.each([1001, 1750, 9161])( + "rejects a line chart with %i segments before invoking its renderer", + (seriesCount) => { + const { dataView } = recordedDataFacade( + ReferenceRecordings.Scenarios.LineChart + .SingleMeasureWithTrendByAndSegmentBy as unknown as ScenarioRecording, + ); + const onDataTooLarge = vi.fn(); + const renderer = vi.fn().mockReturnValue(
); + const segmentHeaders = Array.from({ length: seriesCount }, (_, index) => ({ + attributeHeaderItem: { uri: `segment-${index}`, name: `Segment ${index}` }, + })); + + render( + createComponent({ + dataView: localDataView({ + definition: dataView.definition, + response: { executionResponse: { dimensions: dataView.result.dimensions } }, + result: { + executionResult: { + data: Array.from({ length: seriesCount }, () => dataView.data[0]), + headerItems: [[segmentHeaders], dataView.headerItems[1]], + paging: { + count: [seriesCount, dataView.count[1]], + offset: [0, 0], + total: [seriesCount, dataView.totalCount[1]], + }, + }, + }, + }), + config: { type: VisualizationTypes.LINE }, + onDataTooLarge, + renderer, + }), + ); + + expect(onDataTooLarge).toHaveBeenCalledExactlyOnceWith( + expect.anything(), + `Series limit: 1000 actual: ${seriesCount}.`, + ); + expect(renderer).not.toHaveBeenCalled(); + }, + ); + it("should be invoked if data series is over limit", () => { const onDataTooLarge = vi.fn(); const props = { diff --git a/libs/sdk-ui-charts/src/highcharts/ChartTransformation.tsx b/libs/sdk-ui-charts/src/highcharts/ChartTransformation.tsx index 4e20f6640e3..2e5e8447728 100644 --- a/libs/sdk-ui-charts/src/highcharts/ChartTransformation.tsx +++ b/libs/sdk-ui-charts/src/highcharts/ChartTransformation.tsx @@ -196,7 +196,7 @@ export const ChartTransformation = memo( if (validationResult.dataTooLarge) { // always force onDataTooLarge error handling invariant(onDataTooLarge, "Visualization's onDataTooLarge callback is missing."); - onDataTooLarge(chartOptions, getDataTooLargeErrorMessage(config.limits!, chartOptions)); + onDataTooLarge(chartOptions, getDataTooLargeErrorMessage(config.limits, chartOptions)); } else if (validationResult.hasNegativeValue) { // ignore hasNegativeValue if validation already fails on dataTooLarge. // hasNegativeValue is true for pie-family charts (any negative value) and for Mekko diff --git a/libs/sdk-ui-charts/src/highcharts/chartTypes/_chartOptions/chartLimits.test.ts b/libs/sdk-ui-charts/src/highcharts/chartTypes/_chartOptions/chartLimits.test.ts index a0adeaee5e1..364942c3563 100644 --- a/libs/sdk-ui-charts/src/highcharts/chartTypes/_chartOptions/chartLimits.test.ts +++ b/libs/sdk-ui-charts/src/highcharts/chartTypes/_chartOptions/chartLimits.test.ts @@ -8,7 +8,7 @@ import { type ChartType, VisualizationTypes } from "@gooddata/sdk-ui"; import { type IChartOptions } from "../../typings/unsafe.js"; -import { getIsFilteringRecommended, validateData } from "./chartLimits.js"; +import { getDataTooLargeErrorMessage, getIsFilteringRecommended, validateData } from "./chartLimits.js"; function makeChartOptions({ type, @@ -72,11 +72,11 @@ describe("stacked column/bar series limit", () => { expect(validateData(undefined, options).dataTooLarge).toBe(false); }); - it("does NOT affect LINE chart with 1200 series", () => { + it("does NOT affect AREA chart with 1200 series", () => { const options = makeChartOptions({ - type: VisualizationTypes.LINE, + type: VisualizationTypes.AREA, seriesCount: 1200, - stacking: "normal", // would not normally apply to line, but guards against future regression + stacking: "normal", }); expect(validateData(undefined, options).dataTooLarge).toBe(false); }); @@ -103,6 +103,60 @@ describe("stacked column/bar series limit", () => { }); }); +describe("line chart series limit", () => { + it.each([ + [144, false], + [1000, false], + [1001, true], + [1750, true], + [2500, true], + [9161, true], + ])("validates %i line series as dataTooLarge: %s", (seriesCount, dataTooLarge) => { + const options = makeChartOptions({ + type: VisualizationTypes.LINE, + seriesCount, + stacking: null, + dataPointsPerSeries: 4, + }); + + expect(validateData(undefined, options).dataTooLarge).toBe(dataTooLarge); + }); + + it("reports the exceeded series limit in the error message", () => { + const options = makeChartOptions({ + type: VisualizationTypes.LINE, + seriesCount: 1750, + stacking: null, + }); + + expect(getDataTooLargeErrorMessage(undefined, options)).toBe("Series limit: 1000 actual: 1750."); + }); + + it("keeps the default category limit for line charts", () => { + const options = makeChartOptions({ + type: VisualizationTypes.LINE, + seriesCount: 1, + stacking: null, + dataPointsPerSeries: 10001, + }); + + expect(validateData(undefined, options).dataTooLarge).toBe(true); + }); + + it("allows consumer-supplied limits to replace the line defaults", () => { + const options = makeChartOptions({ + type: VisualizationTypes.LINE, + seriesCount: 1750, + stacking: null, + }); + const limits = { series: 2000, categories: 10000 }; + + expect(validateData(limits, options).dataTooLarge).toBe(false); + expect(getDataTooLargeErrorMessage(limits, options)).toBe(""); + expect(validateData({ series: 100 }, options).dataTooLarge).toBe(true); + }); +}); + describe("mekko negligible column width", () => { const mekkoOptions = (widths: number[]): IChartOptions => ({ type: VisualizationTypes.MEKKO, diff --git a/libs/sdk-ui-charts/src/highcharts/chartTypes/_chartOptions/chartLimits.ts b/libs/sdk-ui-charts/src/highcharts/chartTypes/_chartOptions/chartLimits.ts index 26557978518..613a6c55e26 100644 --- a/libs/sdk-ui-charts/src/highcharts/chartTypes/_chartOptions/chartLimits.ts +++ b/libs/sdk-ui-charts/src/highcharts/chartTypes/_chartOptions/chartLimits.ts @@ -9,6 +9,7 @@ import { DEFAULT_DATA_POINTS_LIMIT, DEFAULT_SERIES_LIMIT, HEATMAP_DATA_POINTS_LIMIT, + LINE_CHART_SERIES_LIMIT, PIE_CHART_LIMIT, SANKEY_CHART_DATA_POINT_LIMIT, SANKEY_CHART_NODE_LIMIT, @@ -46,6 +47,11 @@ export interface IValidationResult { function getChartLimits(type: string | undefined, chartOptions?: IChartOptions): IChartLimits { const isStacked = !!chartOptions?.stacking; switch (type) { + case VisualizationTypes.LINE: + return { + series: LINE_CHART_SERIES_LIMIT, + categories: DEFAULT_CATEGORIES_LIMIT, + }; case VisualizationTypes.SCATTER: return { series: DEFAULT_SERIES_LIMIT, @@ -198,7 +204,7 @@ export function validateData( const type = chartOptions.type; const { isViewByTwoAttributes } = chartOptions; // Consumer-supplied limits intentionally replace per-type defaults. - // The stacked column/bar series cap applies only when default limits are used. + // Chart-specific series caps apply only when default limits are used. const finalLimits = limits || getChartLimits(type, chartOptions); let dataToValidate = chartOptions.data; if (isTreemap(type)) { @@ -235,7 +241,7 @@ export function getIsFilteringRecommended(chartOptions: IChartOptions): boolean /** * Creates a message for onDataTooLarge error, which contains the limits which were exceeded. */ -export function getDataTooLargeErrorMessage(limits: IChartLimits, chartOptions: IChartOptions) { +export function getDataTooLargeErrorMessage(limits: IChartLimits | undefined, chartOptions: IChartOptions) { const { type, isViewByTwoAttributes } = chartOptions; const { series: seriesLimit, diff --git a/libs/sdk-ui-charts/src/highcharts/constants/limits.ts b/libs/sdk-ui-charts/src/highcharts/constants/limits.ts index 1e97b70938b..1a55b024695 100644 --- a/libs/sdk-ui-charts/src/highcharts/constants/limits.ts +++ b/libs/sdk-ui-charts/src/highcharts/constants/limits.ts @@ -11,6 +11,9 @@ export const WATERFALL_CHART_DATA_POINT_LIMIT = 10000; // Stacked column/bar charts freeze the main thread well before the generic 10k series cap // (Highcharts can't draw more than ~1500 stacks), so they need a tighter limit than other chart types. export const STACKED_COLUMN_BAR_SERIES_LIMIT = 1000; +// Highcharts repeatedly scans SVG siblings while inserting series groups and exposing them to assistive +// technology. That work grows quadratically with series count; cap it before the browser becomes unresponsive. +export const LINE_CHART_SERIES_LIMIT = 1000; // Column/bar charts render one SVG element per data point, and the point count is categories x series. // That total can hang the browser even when neither dimension alone hits its cap, so cap the total too. export const COLUMN_BAR_TOTAL_DATA_POINTS_LIMIT = 6000; diff --git a/libs/sdk-ui-dashboard/api/sdk-ui-dashboard.api.md b/libs/sdk-ui-dashboard/api/sdk-ui-dashboard.api.md index 842fe70760c..df7c842e498 100644 --- a/libs/sdk-ui-dashboard/api/sdk-ui-dashboard.api.md +++ b/libs/sdk-ui-dashboard/api/sdk-ui-dashboard.api.md @@ -1172,7 +1172,7 @@ export type DashboardConfig = { exportMetadata?: Record; disableCrossFiltering?: boolean; disableUserFilterReset?: boolean; - disablePersistentFiltersAcrossTabs?: boolean; + enablePersistentFiltersAcrossTabs?: boolean; hideAddTabButton?: boolean; openAutomationOnLoad?: boolean; focusObject?: IDashboardFocusObject; @@ -1245,7 +1245,7 @@ export type DashboardDeinitializedPayload = { export type DashboardDensity = "comfortable" | "compact"; // @public (undocumented) -export type DashboardDescriptor = Pick & IAccessControlAware; +export type DashboardDescriptor = Pick & IAccessControlAware; // @public (undocumented) export type DashboardDispatch = Dispatch; @@ -5996,12 +5996,12 @@ export interface IDashboardSettingsApplyPayload { // (undocumented) disableFilterViews: boolean; // (undocumented) - disablePersistentFiltersAcrossTabs: boolean; - // (undocumented) disableUserFilterReset: boolean; // (undocumented) disableUserFilterSave: boolean; // (undocumented) + enablePersistentFiltersAcrossTabs: boolean; + // (undocumented) evaluationFrequency: string | undefined; // (undocumented) sectionHeadersDateDataSet: ObjRef | undefined; @@ -11000,7 +11000,7 @@ export const metaActions: { setDashboardTitle: ActionCreatorWithPayload; setDashboardTimezoneConfig: ActionCreatorWithOptionalPayload; setDisableCrossFiltering: ActionCreatorWithPayload; - setDisablePersistentFiltersAcrossTabs: ActionCreatorWithPayload; + setEnablePersistentFiltersAcrossTabs: ActionCreatorWithPayload; setDisableUserFilterReset: ActionCreatorWithPayload; setDisableUserFilterSave: ActionCreatorWithPayload; setDisableFilterViews: ActionCreatorWithPayload; @@ -12267,6 +12267,9 @@ export const selectDashboardParametersByTab: DashboardSelector; +// @alpha +export const selectDashboardPersistentFiltersAcrossTabs: DashboardSelector; + // @public export const selectDashboardRef: DashboardSelector; @@ -12363,9 +12366,6 @@ export const selectDeleteVisible: DashboardSelector; // @public export const selectDisableDashboardCrossFiltering: DashboardSelector; -// @alpha -export const selectDisableDashboardPersistentFiltersAcrossTabs: DashboardSelector; - // @public export const selectDisableDashboardUserFilterReset: DashboardSelector; @@ -12471,6 +12471,9 @@ export const selectEnableDashboardFilterGroups: DashboardSelector; // @internal export const selectEnableDashboardPartialRendering: DashboardSelector; +// @alpha +export const selectEnableDashboardPersistentFiltersAcrossTabs: DashboardSelector; + // @alpha export const selectEnableDashboardSectionHeadersDateDataSet: DashboardSelector; diff --git a/libs/sdk-ui-dashboard/package.json b/libs/sdk-ui-dashboard/package.json index 8daf6f488c2..f48658d0107 100644 --- a/libs/sdk-ui-dashboard/package.json +++ b/libs/sdk-ui-dashboard/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-ui-dashboard", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData SDK - Dashboard Component", "license": "LicenseRef-LICENSE", "author": "GoodData Corporation", diff --git a/libs/sdk-ui-dashboard/src/index.ts b/libs/sdk-ui-dashboard/src/index.ts index 15dc7342410..eef0f6c7aa8 100644 --- a/libs/sdk-ui-dashboard/src/index.ts +++ b/libs/sdk-ui-dashboard/src/index.ts @@ -1408,6 +1408,7 @@ export { selectEnableFiscalCalendars, selectEnableSecondGranularities, selectEnableTimezoneChange, + selectEnableDashboardPersistentFiltersAcrossTabs, selectActiveCalendars, selectEnableDashboardFilterGroups, selectEnableDashboardDensitySetting, @@ -1802,7 +1803,7 @@ export { selectIsDashboardPrivate, selectDashboardWorkingDefinition, selectDisableDashboardCrossFiltering, - selectDisableDashboardPersistentFiltersAcrossTabs, + selectDashboardPersistentFiltersAcrossTabs, selectDisableDashboardUserFilterReset, selectDisableDashboardUserFilterSave, selectDisableFilterViews, diff --git a/libs/sdk-ui-dashboard/src/model/DashboardTester.ts b/libs/sdk-ui-dashboard/src/model/DashboardTester.ts index 012792b5063..ed1ea516103 100644 --- a/libs/sdk-ui-dashboard/src/model/DashboardTester.ts +++ b/libs/sdk-ui-dashboard/src/model/DashboardTester.ts @@ -27,7 +27,11 @@ import { type DashboardEvents } from "./events/index.js"; import { HeadlessDashboard, type IHeadlessDashboardConfig } from "./headlessDashboard/HeadlessDashboard.js"; import { type IDashboardQueryService } from "./store/_infra/queryService.js"; import { type DashboardState } from "./store/types.js"; -import { type DashboardContext, type DashboardModelCustomizationFns } from "./types/commonTypes.js"; +import { + type DashboardConfig, + type DashboardContext, + type DashboardModelCustomizationFns, +} from "./types/commonTypes.js"; type DashboardTesterConfig = { queryServices?: IDashboardQueryService[]; @@ -94,6 +98,7 @@ export class DashboardTester extends HeadlessDashboard { public static forNewDashboard( testerConfig?: DashboardTesterConfig, backendConfig?: RecordedBackendConfig, + contextConfig?: DashboardConfig, ): DashboardTester { const ctx: DashboardContext = { backend: recordedBackend(ReferenceRecordings.Recordings, { @@ -104,6 +109,7 @@ export class DashboardTester extends HeadlessDashboard { }, }), workspace: "reference-workspace", + config: contextConfig, }; return new DashboardTester(ctx, testerConfig); @@ -253,6 +259,14 @@ export interface IPreloadedTesterOptions { * Default is no customization. */ customizationFns?: DashboardModelCustomizationFns; + + /** + * Config to put on the dashboard context of a new dashboard (e.g. `initialContent`). + * Ignored when a dashboard identifier is provided. + * + * Default is no config. + */ + newDashboardContextConfig?: DashboardConfig; } /** @@ -298,6 +312,7 @@ export async function preloadedTesterFactory( backendConfig, customCapabilities, customizationFns, + newDashboardContextConfig, } = options; const tester = identifier @@ -316,6 +331,7 @@ export async function preloadedTesterFactory( { ...backendConfig, }, + newDashboardContextConfig, ); tester.dispatch(initCommand); diff --git a/libs/sdk-ui-dashboard/src/model/commandHandlers/dashboard/common/stateInitializers.ts b/libs/sdk-ui-dashboard/src/model/commandHandlers/dashboard/common/stateInitializers.ts index 07d433024d4..e3ada9daf0b 100644 --- a/libs/sdk-ui-dashboard/src/model/commandHandlers/dashboard/common/stateInitializers.ts +++ b/libs/sdk-ui-dashboard/src/model/commandHandlers/dashboard/common/stateInitializers.ts @@ -266,6 +266,9 @@ export function* actionsToInitializeNewDashboard( displayForms, ); + const enablePersistentFiltersAcrossTabsAction = + settings?.enableDashboardPersistentFiltersAcrossTabs ?? false; + const tabs: IDashboardTab[] = dashboard?.tabs ?? [ { localIdentifier: DEFAULT_TAB_ID, @@ -339,6 +342,9 @@ export function* actionsToInitializeNewDashboard( insightsActions.setInsights(insights), drillActions.resetCrossFiltering(), ]), + ...(enablePersistentFiltersAcrossTabsAction + ? [metaActions.setEnablePersistentFiltersAcrossTabs(true)] + : []), ], dashboard: initialContent ? dashboard : undefined, insights: initialContent ? insights : [], diff --git a/libs/sdk-ui-dashboard/src/model/commandHandlers/dashboard/initializeDashboardHandler/__snapshots__/handler.test.ts.snap b/libs/sdk-ui-dashboard/src/model/commandHandlers/dashboard/initializeDashboardHandler/__snapshots__/handler.test.ts.snap index cc2be22e5e3..4807f3c97b2 100644 --- a/libs/sdk-ui-dashboard/src/model/commandHandlers/dashboard/initializeDashboardHandler/__snapshots__/handler.test.ts.snap +++ b/libs/sdk-ui-dashboard/src/model/commandHandlers/dashboard/initializeDashboardHandler/__snapshots__/handler.test.ts.snap @@ -472,10 +472,10 @@ exports[`initialize dashboard handler > for any dashboard > should resolve confi }, "disableCrossFiltering": false, "disableDefaultDrills": false, - "disablePersistentFiltersAcrossTabs": false, "disableUserFilterReset": false, "enableFilterValuesResolutionInDrillEvents": false, "enableInsightEditing": false, + "enablePersistentFiltersAcrossTabs": true, "externalRecipient": undefined, "hideAddTabButton": false, "hideSaveAsNewButton": false, diff --git a/libs/sdk-ui-dashboard/src/model/commandHandlers/dashboard/initializeDashboardHandler/handler.test.ts b/libs/sdk-ui-dashboard/src/model/commandHandlers/dashboard/initializeDashboardHandler/handler.test.ts index 8a95967cdf4..a83597baabf 100644 --- a/libs/sdk-ui-dashboard/src/model/commandHandlers/dashboard/initializeDashboardHandler/handler.test.ts +++ b/libs/sdk-ui-dashboard/src/model/commandHandlers/dashboard/initializeDashboardHandler/handler.test.ts @@ -23,7 +23,10 @@ import { initializeDashboard } from "../../../commands/dashboard.js"; import { DashboardTester, preloadedTesterFactory } from "../../../DashboardTester.js"; import { type DashboardInitialized } from "../../../events/dashboard.js"; import { selectConfig } from "../../../store/config/configSelectors.js"; -import { selectPersistedDashboard } from "../../../store/meta/metaSelectors.js"; +import { + selectEnablePersistentFiltersAcrossTabs, + selectPersistedDashboard, +} from "../../../store/meta/metaSelectors.js"; import { selectPermissions } from "../../../store/permissions/permissionsSelectors.js"; import { selectEffectiveDateFilterConfig } from "../../../store/tabs/dateFilterConfig/dateFilterConfigSelectors.js"; import { @@ -32,6 +35,7 @@ import { selectFilterContextIdentity, } from "../../../store/tabs/filterContext/filterContextSelectors.js"; import { selectLayout } from "../../../store/tabs/layout/layoutSelectors.js"; +import { PivotTableWithDateFilter } from "../../../tests/Insights.test.helpers.js"; import { type PrivateDashboardContext } from "../../../types/commonTypes.js"; import { EmptyDashboardLayout } from "../../dashboard/common/dashboardInitialize.js"; @@ -282,6 +286,51 @@ describe("initialize dashboard handler", () => { }, ); + describe("persistent filters across tabs default for a new dashboard", () => { + async function initNewDashboard(featureEnabled: boolean, withInitialContent: boolean) { + let Tester: DashboardTester; + await preloadedTesterFactory( + (tester) => { + Tester = tester; + }, + undefined, + { + backendConfig: { + globalSettings: { enableDashboardPersistentFiltersAcrossTabs: featureEnabled }, + }, + newDashboardContextConfig: withInitialContent + ? { initialContent: [{ visualizationContent: PivotTableWithDateFilter }] } + : undefined, + }, + ); + return Tester!; + } + + it.each([ + ["without initial content", false], + ["with initial content", true], + ])( + "should enable persistent filters across tabs %s when the feature is enabled", + async (_, withInitialContent) => { + const Tester = await initNewDashboard(true, withInitialContent); + + expect(selectEnablePersistentFiltersAcrossTabs(Tester.state())).toBe(true); + }, + ); + + it.each([ + ["without initial content", false], + ["with initial content", true], + ])( + "should keep persistent filters across tabs off %s when the feature is disabled", + async (_, withInitialContent) => { + const Tester = await initNewDashboard(false, withInitialContent); + + expect(selectEnablePersistentFiltersAcrossTabs(Tester.state())).toBe(false); + }, + ); + }); + describe("absolute date filter granularity derivation for a new dashboard", () => { const dateFilterConfig = { ref: idRef("newDashboardDateFilterConfig"), diff --git a/libs/sdk-ui-dashboard/src/model/commandHandlers/dashboard/initializeDashboardHandler/index.ts b/libs/sdk-ui-dashboard/src/model/commandHandlers/dashboard/initializeDashboardHandler/index.ts index 94d5c49d3af..469eb404928 100644 --- a/libs/sdk-ui-dashboard/src/model/commandHandlers/dashboard/initializeDashboardHandler/index.ts +++ b/libs/sdk-ui-dashboard/src/model/commandHandlers/dashboard/initializeDashboardHandler/index.ts @@ -368,7 +368,7 @@ function* loadExistingDashboard( // same rule as selectEnablePersistentFiltersAcrossTabs, which cannot be selected before init const persistFiltersAcrossTabs = !!config.settings.enableDashboardPersistentFiltersAcrossTabs && - loadedDashboard.disablePersistentFiltersAcrossTabs === false; + loadedDashboard.enablePersistentFiltersAcrossTabs === true; const filterViewOptions = { persistFiltersAcrossTabs, activeTabLocalIdentifier: resolvedActiveTabId }; const preloadedAttributeFilters: SagaReturnType = yield call( diff --git a/libs/sdk-ui-dashboard/src/model/commandHandlers/dashboard/initializeDashboardHandler/resolveDashboardConfig.ts b/libs/sdk-ui-dashboard/src/model/commandHandlers/dashboard/initializeDashboardHandler/resolveDashboardConfig.ts index c74bacdae38..3118cfff9d6 100644 --- a/libs/sdk-ui-dashboard/src/model/commandHandlers/dashboard/initializeDashboardHandler/resolveDashboardConfig.ts +++ b/libs/sdk-ui-dashboard/src/model/commandHandlers/dashboard/initializeDashboardHandler/resolveDashboardConfig.ts @@ -246,7 +246,7 @@ const CONFIG_DEFAULTS: Required< | "hideShareButton" | "disableCrossFiltering" | "disableUserFilterReset" - | "disablePersistentFiltersAcrossTabs" + | "enablePersistentFiltersAcrossTabs" | "widgetsOverlay" | "hideAddTabButton" > @@ -268,7 +268,7 @@ const CONFIG_DEFAULTS: Required< hideShareButton: false, disableCrossFiltering: false, disableUserFilterReset: false, - disablePersistentFiltersAcrossTabs: false, + enablePersistentFiltersAcrossTabs: true, widgetsOverlay: {}, externalRecipient: undefined, hideAddTabButton: false, diff --git a/libs/sdk-ui-dashboard/src/model/commandHandlers/filterContext/dateFilter/changeDateFilterSelectionHandler.test.ts b/libs/sdk-ui-dashboard/src/model/commandHandlers/filterContext/dateFilter/changeDateFilterSelectionHandler.test.ts index 012d73a02fe..2219f0fc542 100644 --- a/libs/sdk-ui-dashboard/src/model/commandHandlers/filterContext/dateFilter/changeDateFilterSelectionHandler.test.ts +++ b/libs/sdk-ui-dashboard/src/model/commandHandlers/filterContext/dateFilter/changeDateFilterSelectionHandler.test.ts @@ -105,7 +105,7 @@ describe("changeDateFilterSelectionHandler", () => { settings: { ...config.settings, enableDashboardPersistentFiltersAcrossTabs: true }, }), ); - Tester.dispatch(metaActions.setDisablePersistentFiltersAcrossTabs(false)); + Tester.dispatch(metaActions.setEnablePersistentFiltersAcrossTabs(true)); }); it("emits one filter context changed event for the changed source tab", async () => { diff --git a/libs/sdk-ui-dashboard/src/model/commandHandlers/parameters/changeParameterValuesHandler.test.ts b/libs/sdk-ui-dashboard/src/model/commandHandlers/parameters/changeParameterValuesHandler.test.ts index 977e21f4f5d..6b7069f88fb 100644 --- a/libs/sdk-ui-dashboard/src/model/commandHandlers/parameters/changeParameterValuesHandler.test.ts +++ b/libs/sdk-ui-dashboard/src/model/commandHandlers/parameters/changeParameterValuesHandler.test.ts @@ -166,7 +166,7 @@ describe("changeParameterValues handler", () => { }, }), ); - Tester.dispatch(metaActions.setDisablePersistentFiltersAcrossTabs(false)); + Tester.dispatch(metaActions.setEnablePersistentFiltersAcrossTabs(true)); } function appendTab(parameters?: { parameter: IDashboardParameter; runtimeOverride: number }[]): void { diff --git a/libs/sdk-ui-dashboard/src/model/commandHandlers/tabs/createDashboardTabHandler.test.ts b/libs/sdk-ui-dashboard/src/model/commandHandlers/tabs/createDashboardTabHandler.test.ts index e8f54682228..e73b3f00dd2 100644 --- a/libs/sdk-ui-dashboard/src/model/commandHandlers/tabs/createDashboardTabHandler.test.ts +++ b/libs/sdk-ui-dashboard/src/model/commandHandlers/tabs/createDashboardTabHandler.test.ts @@ -70,7 +70,7 @@ describe("createDashboardTabHandler", () => { }, ); - Tester.dispatch(metaActions.setDisablePersistentFiltersAcrossTabs(false)); + Tester.dispatch(metaActions.setEnablePersistentFiltersAcrossTabs(true)); } function createTab() { diff --git a/libs/sdk-ui-dashboard/src/model/commandHandlers/tabs/switchDashboardTabHandler.test.ts b/libs/sdk-ui-dashboard/src/model/commandHandlers/tabs/switchDashboardTabHandler.test.ts index 2dee0aabf27..449f9560354 100644 --- a/libs/sdk-ui-dashboard/src/model/commandHandlers/tabs/switchDashboardTabHandler.test.ts +++ b/libs/sdk-ui-dashboard/src/model/commandHandlers/tabs/switchDashboardTabHandler.test.ts @@ -128,7 +128,7 @@ describe("switchDashboardTab handler", () => { settings: { ...config.settings, enableDashboardPersistentFiltersAcrossTabs: true }, }), ); - Tester.dispatch(metaActions.setDisablePersistentFiltersAcrossTabs(false)); + Tester.dispatch(metaActions.setEnablePersistentFiltersAcrossTabs(true)); } async function switchTo(tabId: string): Promise { diff --git a/libs/sdk-ui-dashboard/src/model/store/meta/metaReducers.ts b/libs/sdk-ui-dashboard/src/model/store/meta/metaReducers.ts index 884c876b5ad..d9b93469b53 100644 --- a/libs/sdk-ui-dashboard/src/model/store/meta/metaReducers.ts +++ b/libs/sdk-ui-dashboard/src/model/store/meta/metaReducers.ts @@ -36,7 +36,7 @@ const setMeta: MetaReducer> = (state, action) => { disableUserFilterReset: descriptorDashboard.disableUserFilterReset, disableUserFilterSave: descriptorDashboard.disableUserFilterSave, disableFilterViews: descriptorDashboard.disableFilterViews, - disablePersistentFiltersAcrossTabs: descriptorDashboard.disablePersistentFiltersAcrossTabs, + enablePersistentFiltersAcrossTabs: descriptorDashboard.enablePersistentFiltersAcrossTabs, sectionHeadersDateDataSet: descriptorDashboard.sectionHeadersDateDataSet, timezoneConfig: descriptorDashboard.timezoneConfig, } @@ -67,10 +67,10 @@ const setDashboardTimezoneConfig: MetaReducer> = (state, action) => { +const setEnablePersistentFiltersAcrossTabs: MetaReducer> = (state, action) => { invariant(state.descriptor); - state.descriptor.disablePersistentFiltersAcrossTabs = action.payload; + state.descriptor.enablePersistentFiltersAcrossTabs = action.payload; }; const setDisableUserFilterReset: MetaReducer> = (state, action) => { @@ -108,7 +108,7 @@ export const metaReducers = { setDashboardTitle, setDashboardTimezoneConfig, setDisableCrossFiltering, - setDisablePersistentFiltersAcrossTabs, + setEnablePersistentFiltersAcrossTabs, setDisableUserFilterReset, setDisableUserFilterSave, setDisableFilterViews, diff --git a/libs/sdk-ui-dashboard/src/model/store/meta/metaSelectors.test.ts b/libs/sdk-ui-dashboard/src/model/store/meta/metaSelectors.test.ts index 017e56e3c42..832423eda4a 100644 --- a/libs/sdk-ui-dashboard/src/model/store/meta/metaSelectors.test.ts +++ b/libs/sdk-ui-dashboard/src/model/store/meta/metaSelectors.test.ts @@ -74,24 +74,24 @@ describe("selectPersistedEnablePersistentFiltersAcrossTabs", () => { ); }); - function persistSetting(disablePersistentFiltersAcrossTabs: boolean): void { + function persistSetting(enablePersistentFiltersAcrossTabs: boolean): void { const dashboard = selectPersistedDashboard(Tester.state())!; Tester.dispatch( - metaActions.setMeta({ dashboard: { ...dashboard, disablePersistentFiltersAcrossTabs } }), + metaActions.setMeta({ dashboard: { ...dashboard, enablePersistentFiltersAcrossTabs } }), ); } it("ignores an unsaved enablement", () => { - persistSetting(true); - Tester.dispatch(metaActions.setDisablePersistentFiltersAcrossTabs(false)); + persistSetting(false); + Tester.dispatch(metaActions.setEnablePersistentFiltersAcrossTabs(true)); expect(Tester.select(selectEnablePersistentFiltersAcrossTabs)).toBe(true); expect(Tester.select(selectPersistedEnablePersistentFiltersAcrossTabs)).toBe(false); }); it("ignores an unsaved disablement", () => { - persistSetting(false); - Tester.dispatch(metaActions.setDisablePersistentFiltersAcrossTabs(true)); + persistSetting(true); + Tester.dispatch(metaActions.setEnablePersistentFiltersAcrossTabs(false)); expect(Tester.select(selectEnablePersistentFiltersAcrossTabs)).toBe(false); expect(Tester.select(selectPersistedEnablePersistentFiltersAcrossTabs)).toBe(true); diff --git a/libs/sdk-ui-dashboard/src/model/store/meta/metaSelectors.ts b/libs/sdk-ui-dashboard/src/model/store/meta/metaSelectors.ts index 2b4bc3e83b0..3317539e356 100644 --- a/libs/sdk-ui-dashboard/src/model/store/meta/metaSelectors.ts +++ b/libs/sdk-ui-dashboard/src/model/store/meta/metaSelectors.ts @@ -618,15 +618,17 @@ export const selectDisableDashboardCrossFiltering: DashboardSelector = ); /** - * Selects whether dashboard persisted filters are disabled. + * Selects whether the dashboard keeps filter selections when switching tabs. + * + * @remarks + * Missing means a dashboard saved before this setting existed: the feature stays off. * * @alpha */ -export const selectDisableDashboardPersistentFiltersAcrossTabs: DashboardSelector = createSelector( +export const selectDashboardPersistentFiltersAcrossTabs: DashboardSelector = createSelector( selectDashboardDescriptor, (state) => { - // Missing means a dashboard saved before this setting existed: the feature stays off. - return state.disablePersistentFiltersAcrossTabs ?? true; + return state.enablePersistentFiltersAcrossTabs ?? false; }, ); @@ -637,12 +639,9 @@ export const selectDisableDashboardPersistentFiltersAcrossTabs: DashboardSelecto */ export const selectEnablePersistentFiltersAcrossTabs: DashboardSelector = createSelector( selectEnableDashboardPersistentFiltersAcrossTabs, - selectDisableDashboardPersistentFiltersAcrossTabs, - (enableDashboardPersistentFiltersAcrossTabs, disableDashboardPersistentFiltersAcrossTabs) => { - return ( - enableDashboardPersistentFiltersAcrossTabs && - disableDashboardPersistentFiltersAcrossTabs === false - ); + selectDashboardPersistentFiltersAcrossTabs, + (enableDashboardPersistentFiltersAcrossTabs, persistentFiltersAcrossTabs) => { + return enableDashboardPersistentFiltersAcrossTabs && persistentFiltersAcrossTabs; }, ); @@ -650,8 +649,8 @@ export const selectEnablePersistentFiltersAcrossTabs: DashboardSelector * Selects persisted persistent filters across tabs - that is the value that was used to initialize * the dashboard state during the initial load of the dashboard. */ -const selectPersistedDashboardDisablePersistentFiltersAcrossTabs = createSelector(selectSelf, (state) => { - return state.persistedDashboard?.disablePersistentFiltersAcrossTabs ?? true; +const selectPersistedDashboardPersistentFiltersAcrossTabs = createSelector(selectSelf, (state) => { + return state.persistedDashboard?.enablePersistentFiltersAcrossTabs ?? false; }); /** @@ -662,25 +661,24 @@ const selectPersistedDashboardDisablePersistentFiltersAcrossTabs = createSelecto */ export const selectPersistedEnablePersistentFiltersAcrossTabs: DashboardSelector = createSelector( selectEnableDashboardPersistentFiltersAcrossTabs, - selectPersistedDashboardDisablePersistentFiltersAcrossTabs, - (enableDashboardPersistentFiltersAcrossTabs, persistedDisablePersistentFiltersAcrossTabs) => { - return ( - enableDashboardPersistentFiltersAcrossTabs && - persistedDisablePersistentFiltersAcrossTabs === false - ); + selectPersistedDashboardPersistentFiltersAcrossTabs, + (enableDashboardPersistentFiltersAcrossTabs, persistedEnablePersistentFiltersAcrossTabs) => { + return enableDashboardPersistentFiltersAcrossTabs && persistedEnablePersistentFiltersAcrossTabs; }, ); /** * Selects a boolean indication if the dashboard has any changes to the persistent filters across tabs compared to the persisted version (if any) + * Selects a boolean indication if the dashboard has any changes to persistent filters across tabs + * compared to the persisted version (if any). * * @internal */ -export const selectIsDisablePersistentFiltersAcrossTabsChanged: DashboardSelector = createSelector( - selectPersistedDashboardDisablePersistentFiltersAcrossTabs, - selectDisableDashboardPersistentFiltersAcrossTabs, - (persistedDisablePersistentFiltersAcrossTabs, currentDisablePersistentFiltersAcrossTabs) => { - return persistedDisablePersistentFiltersAcrossTabs !== currentDisablePersistentFiltersAcrossTabs; +export const selectIsPersistentFiltersAcrossTabsChanged: DashboardSelector = createSelector( + selectPersistedDashboardPersistentFiltersAcrossTabs, + selectDashboardPersistentFiltersAcrossTabs, + (persistedPersistentFiltersAcrossTabs, currentPersistentFiltersAcrossTabs) => { + return persistedPersistentFiltersAcrossTabs !== currentPersistentFiltersAcrossTabs; }, ); @@ -1307,7 +1305,7 @@ export const selectIsDashboardDirty: DashboardSelector = createSelector selectIsDisableUserFilterResetChanged, selectIsDisableUserFilterSaveChanged, selectIsDisableFilterViewsChanged, - selectIsDisablePersistentFiltersAcrossTabsChanged, + selectIsPersistentFiltersAcrossTabsChanged, selectEvaluationFrequencyChanged, selectIsSectionHeadersDateDataSetChanged, selectIsTimezoneConfigChanged, @@ -1324,7 +1322,7 @@ export const selectIsDashboardDirty: DashboardSelector = createSelector isDisableUserFilterResetChanged, isDisableUserFilterSaveChanged, isDisableFilterViewsChanged, - isDisablePersistentFiltersAcrossTabsChanged, + isPersistentFiltersAcrossTabsChanged, isEvaluationFrequencyChanged, isSectionHeadersDateDataSetChanged, isTimezoneConfigChanged, @@ -1344,7 +1342,7 @@ export const selectIsDashboardDirty: DashboardSelector = createSelector isDisableUserFilterResetChanged, isDisableUserFilterSaveChanged, isDisableFilterViewsChanged, - isDisablePersistentFiltersAcrossTabsChanged, + isPersistentFiltersAcrossTabsChanged, isEvaluationFrequencyChanged, isSectionHeadersDateDataSetChanged, isTimezoneConfigChanged, diff --git a/libs/sdk-ui-dashboard/src/model/store/meta/metaState.ts b/libs/sdk-ui-dashboard/src/model/store/meta/metaState.ts index eaa18148cd0..cca13f4880a 100644 --- a/libs/sdk-ui-dashboard/src/model/store/meta/metaState.ts +++ b/libs/sdk-ui-dashboard/src/model/store/meta/metaState.ts @@ -14,7 +14,7 @@ export type DashboardDescriptor = Pick< | "disableUserFilterReset" | "disableUserFilterSave" | "disableFilterViews" - | "disablePersistentFiltersAcrossTabs" + | "enablePersistentFiltersAcrossTabs" | "evaluationFrequency" | "sectionHeadersDateDataSet" | "timezoneConfig" diff --git a/libs/sdk-ui-dashboard/src/model/types/commonTypes.ts b/libs/sdk-ui-dashboard/src/model/types/commonTypes.ts index 3fc655e9541..76569a67ba3 100644 --- a/libs/sdk-ui-dashboard/src/model/types/commonTypes.ts +++ b/libs/sdk-ui-dashboard/src/model/types/commonTypes.ts @@ -407,13 +407,13 @@ export type DashboardConfig = { disableUserFilterReset?: boolean; /** - * Disable persisted filters + * Enable persistent filters across tabs. * * @remarks - * If set to true, persistent filters across tabs will be disabled even if the dashboard is configured to support it. - * If set to false or not set, persisted filters will be enabled if the dashboard is configured to support it. + * If set to true, filter selections are kept when switching tabs. + * If set to false or not set, each tab keeps its own filter selection. */ - disablePersistentFiltersAcrossTabs?: boolean; + enablePersistentFiltersAcrossTabs?: boolean; /** * Hide the add tab button in dashboard edit mode. diff --git a/libs/sdk-ui-dashboard/src/presentation/dashboard/DashboardHeader/SettingsDialogProvider.tsx b/libs/sdk-ui-dashboard/src/presentation/dashboard/DashboardHeader/SettingsDialogProvider.tsx index 66ed872caf7..d2cc60555f6 100644 --- a/libs/sdk-ui-dashboard/src/presentation/dashboard/DashboardHeader/SettingsDialogProvider.tsx +++ b/libs/sdk-ui-dashboard/src/presentation/dashboard/DashboardHeader/SettingsDialogProvider.tsx @@ -35,7 +35,7 @@ const useShareDialogDashboardHeader = () => { dispatch(metaActions.setDisableFilterViews(payload.disableFilterViews)); dispatch(metaActions.setDisableUserFilterSave(payload.disableUserFilterSave)); dispatch( - metaActions.setDisablePersistentFiltersAcrossTabs(payload.disablePersistentFiltersAcrossTabs), + metaActions.setEnablePersistentFiltersAcrossTabs(payload.enablePersistentFiltersAcrossTabs), ); dispatch(metaActions.setEvaluationFrequency(payload.evaluationFrequency)); dispatch(metaActions.setSectionHeadersDateDataSet(payload.sectionHeadersDateDataSet)); diff --git a/libs/sdk-ui-dashboard/src/presentation/dashboardSettingsDialog/DefaultDashboardSettingsDialog.tsx b/libs/sdk-ui-dashboard/src/presentation/dashboardSettingsDialog/DefaultDashboardSettingsDialog.tsx index 5612ca16b18..a3941e74097 100644 --- a/libs/sdk-ui-dashboard/src/presentation/dashboardSettingsDialog/DefaultDashboardSettingsDialog.tsx +++ b/libs/sdk-ui-dashboard/src/presentation/dashboardSettingsDialog/DefaultDashboardSettingsDialog.tsx @@ -188,11 +188,11 @@ export function DefaultDashboardSettingsDialog({ tooltip={intl.formatMessage({ id: "filters.configurationPanel.persistentFiltersAcrossTabs.toggle.tooltip", })} - isChecked={!currentData.disablePersistentFiltersAcrossTabs} + isChecked={currentData.enablePersistentFiltersAcrossTabs} onChange={(isChecked: boolean) => { setCurrentData({ ...currentData, - disablePersistentFiltersAcrossTabs: !isChecked, + enablePersistentFiltersAcrossTabs: isChecked, }); }} /> diff --git a/libs/sdk-ui-dashboard/src/presentation/dashboardSettingsDialog/types.ts b/libs/sdk-ui-dashboard/src/presentation/dashboardSettingsDialog/types.ts index 06d0c66ccaf..ba9ecdde9eb 100644 --- a/libs/sdk-ui-dashboard/src/presentation/dashboardSettingsDialog/types.ts +++ b/libs/sdk-ui-dashboard/src/presentation/dashboardSettingsDialog/types.ts @@ -16,7 +16,7 @@ export interface IDashboardSettingsApplyPayload { disableFilterViews: boolean; evaluationFrequency: string | undefined; sectionHeadersDateDataSet: ObjRef | undefined; - disablePersistentFiltersAcrossTabs: boolean; + enablePersistentFiltersAcrossTabs: boolean; timezoneConfig?: IDashboardTimezoneConfig; } diff --git a/libs/sdk-ui-dashboard/src/presentation/dashboardSettingsDialog/useDialogData.ts b/libs/sdk-ui-dashboard/src/presentation/dashboardSettingsDialog/useDialogData.ts index 795431430ad..99e8882ffc5 100644 --- a/libs/sdk-ui-dashboard/src/presentation/dashboardSettingsDialog/useDialogData.ts +++ b/libs/sdk-ui-dashboard/src/presentation/dashboardSettingsDialog/useDialogData.ts @@ -8,9 +8,9 @@ import { normalizeDashboardTimezoneConfig } from "@gooddata/sdk-model"; import { useDashboardSelector } from "../../model/react/DashboardStoreProvider.js"; import { + selectDashboardPersistentFiltersAcrossTabs, selectDashboardTimezoneConfig, selectDisableDashboardCrossFiltering, - selectDisableDashboardPersistentFiltersAcrossTabs, selectDisableDashboardUserFilterReset, selectDisableDashboardUserFilterSave, selectDisableFilterViews, @@ -25,8 +25,9 @@ export function useDialogData() { const disableUserFilterReset = useDashboardSelector(selectDisableDashboardUserFilterReset) ?? false; const disableFilterViews = useDashboardSelector(selectDisableFilterViews) ?? false; const disableUserFilterSave = useDashboardSelector(selectDisableDashboardUserFilterSave) ?? false; - const disablePersistentFiltersAcrossTabs = - useDashboardSelector(selectDisableDashboardPersistentFiltersAcrossTabs) ?? true; + const enablePersistentFiltersAcrossTabs = useDashboardSelector( + selectDashboardPersistentFiltersAcrossTabs, + ); const evaluationFrequency = useDashboardSelector(selectEvaluationFrequency) ?? undefined; const sectionHeadersDateDataSet = useDashboardSelector(selectSectionHeadersDateDataSet) ?? undefined; const timezoneConfig = normalizeDashboardTimezoneConfig( @@ -38,7 +39,7 @@ export function useDialogData() { disableUserFilterSave, disableUserFilterReset, disableFilterViews, - disablePersistentFiltersAcrossTabs, + enablePersistentFiltersAcrossTabs, evaluationFrequency, sectionHeadersDateDataSet, timezoneConfig, diff --git a/libs/sdk-ui-ext/package.json b/libs/sdk-ui-ext/package.json index f2317dfbc24..43976b2394c 100644 --- a/libs/sdk-ui-ext/package.json +++ b/libs/sdk-ui-ext/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-ui-ext", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData.UI SDK - Extensions", "license": "LicenseRef-LICENSE", "author": "GoodData Corporation", diff --git a/libs/sdk-ui-filters/package.json b/libs/sdk-ui-filters/package.json index 5bb2c8524c0..922b49e94ea 100644 --- a/libs/sdk-ui-filters/package.json +++ b/libs/sdk-ui-filters/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-ui-filters", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData.UI SDK - Filter Components", "license": "MIT", "author": "GoodData Corporation", diff --git a/libs/sdk-ui-gen-ai/api/sdk-ui-gen-ai.api.md b/libs/sdk-ui-gen-ai/api/sdk-ui-gen-ai.api.md index 70e112131a7..eae302aa628 100644 --- a/libs/sdk-ui-gen-ai/api/sdk-ui-gen-ai.api.md +++ b/libs/sdk-ui-gen-ai/api/sdk-ui-gen-ai.api.md @@ -282,6 +282,66 @@ export type ChatVisualizationErrorEvent = BaseEvent & { // @public (undocumented) export const clearThreadAction: ActionCreatorWithoutPayload<"messages/clearThreadAction">; +// @alpha +export enum CopilotBuildType { + Dashboard = "dashboard" +} + +// @alpha +export class CopilotHandler { + constructor(configuration: ICopilotConfiguration); + cancelGeneration: (type: TBuild) => void; + definitionApplicationInterrupted: (applicationId: string) => void; + definitionApplicationStarted: (source: ICopilotApplicationSource) => string; + definitionApplied: (applicationId: string, target: ICopilotTarget) => void; + dispose: () => void; + getAppliedSource: (target: ICopilotTarget) => ICopilotSource | undefined; + getReasoningTitle: () => string | undefined; + getSnapshot: () => ICopilotSnapshot; + getStatus: (scope: ICopilotScope) => CopilotStatus; + handleEvent: (event: ChatEvent) => void; + isGenerationPending: (type: TBuild) => boolean; + resetInteraction: () => void; + resetTarget: (target: ICopilotTarget) => void; + setContext: (context: ICopilotContext) => void; + startGeneration: (type: TBuild) => void; + subscribe: (listener: () => void) => (() => void); +} + +// @alpha +export type CopilotInput = { + readonly kind: "event"; + readonly event: ChatEvent; +} | { + readonly kind: "context"; + readonly context: ICopilotContext; +} | { + readonly kind: "startGeneration" | "cancelGeneration"; + readonly type: TBuild; +} | { + readonly kind: "resetInteraction"; +} | { + readonly kind: "resetTarget"; + readonly target: ICopilotTarget; +} | { + readonly kind: "applicationStarted"; + readonly id: string; + readonly source: ICopilotApplicationSource; +} | { + readonly kind: "applicationApplied"; + readonly id: string; + readonly target: ICopilotTarget; +} | { + readonly kind: "applicationInterrupted"; + readonly id: string; +}; + +// @alpha +export type CopilotStatus = "idle" | "generating" | "building" | "waiting"; + +// @alpha +export function createCopilotState(): ICopilotSnapshot; + // @alpha (undocumented) export const dashboardSavedAction: ActionCreatorWithPayload< { conversationId: string; @@ -526,6 +586,104 @@ export interface IChatMessagesGroup { type: "reasoning" | "user" | "assistant" | "error" | "system"; } +// @alpha +export interface ICopilotApplication extends ICopilotApplicationSource { + readonly id: string; + readonly requestRevision: number; + readonly revision: number; +} + +// @alpha +export interface ICopilotApplicationSource { + readonly conversationId?: string; + readonly target: ICopilotTarget; + readonly threadId?: string; +} + +// @alpha +export interface ICopilotConfiguration { + readonly onClarificationQuestions?: (event: ChatClarificationQuestionsEvent) => void; + readonly registrations: readonly ICopilotRegistration[]; +} + +// @alpha +export interface ICopilotContext { + readonly builderTarget?: ICopilotTarget; + readonly isEditing?: boolean; + readonly objectId?: string; + readonly type?: TBuild; +} + +// @alpha +export interface ICopilotInteraction { + readonly build?: { + readonly type: TBuild; + readonly intent: "create" | "edit"; + }; + readonly conversationId?: string; + readonly pendingClarificationIds: readonly string[]; + readonly reasoningTitle?: string; + readonly threadId?: string; + readonly waitingForUserInput: boolean; +} + +// @alpha +export interface ICopilotRegistration { + readonly onApplyDefinition: (event: ChatDefinitionReceivedEvent, applicationId: string, target: ICopilotTarget) => void; + readonly resolveDefinition: (event: ChatDefinitionReceivedEvent, context: ICopilotContext, snapshot: ICopilotSnapshot) => ICopilotTarget | undefined; + readonly skills: readonly AgentCustomSkill[]; + readonly type: TBuild; +} + +// @alpha +export interface ICopilotRequest { + readonly applicationIds: readonly string[]; + readonly awaitingClarification?: boolean; + readonly conversationId?: string; + readonly objectId?: string; + readonly pendingClarificationIds?: readonly string[]; + readonly responseFinished?: boolean; + readonly revision: number; + readonly threadId?: string; + readonly type?: TBuild; +} + +// @alpha +export interface ICopilotScope { + readonly isBuilder?: boolean; + readonly isEditing?: boolean; + readonly objectId?: string; + readonly type: TBuild; +} + +// @alpha +export interface ICopilotSnapshot { + readonly applications: readonly ICopilotApplication[]; + readonly context: ICopilotContext; + readonly generation?: TBuild; + readonly generationRevision?: number; + readonly interaction: ICopilotInteraction; + readonly requests: readonly ICopilotRequest[]; + readonly revision: number; + readonly sources: readonly { + readonly target: ICopilotTarget; + readonly source?: ICopilotSource; + readonly revision: number; + }[]; +} + +// @alpha +export interface ICopilotSource { + readonly conversationId: string; + readonly threadId?: string; +} + +// @alpha +export interface ICopilotTarget { + readonly objectId: string; + readonly type: TBuild; +} + // @alpha export type IGenAIAssistantAgentChooserProps = { agents: GenAIAgent[]; @@ -922,6 +1080,18 @@ conversation: IChatConversationLocal; title: string; }, "messages/renameConversationAction">; +// @alpha +export function selectCopilotGenerationPending(state: ICopilotSnapshot, type: TBuild): boolean; + +// @alpha +export function selectCopilotReasoningTitle(state: ICopilotSnapshot): string | undefined; + +// @alpha +export function selectCopilotSource(state: ICopilotSnapshot, target: ICopilotTarget): ICopilotSource | undefined; + +// @alpha +export function selectCopilotStatus(state: ICopilotSnapshot, scope: ICopilotScope): CopilotStatus; + // @public (undocumented) export const setAmbientUserContextAction: ActionCreatorWithPayload< { userContext?: IGenAIUserContext; @@ -961,6 +1131,12 @@ export type TextContentObject = { title: string; }; +// @alpha +export function transitionCopilot(state: ICopilotSnapshot, input: CopilotInput, configuration: ICopilotConfiguration): ICopilotSnapshot; + +// @alpha +export function useCopilotSnapshot(handler: CopilotHandler): ICopilotSnapshot; + // @internal (undocumented) export function useGenAiChatAvailability(backend: IAnalyticalBackend, workspaceId?: string, enabled?: boolean, canManage?: boolean): boolean; diff --git a/libs/sdk-ui-gen-ai/package.json b/libs/sdk-ui-gen-ai/package.json index d27a566f4d2..06c60c2c5f4 100644 --- a/libs/sdk-ui-gen-ai/package.json +++ b/libs/sdk-ui-gen-ai/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-ui-gen-ai", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData GenAI SDK", "license": "MIT", "author": "GoodData Corporation", diff --git a/libs/sdk-ui-gen-ai/src/copilot/transitions.test.ts b/libs/sdk-ui-gen-ai/src/copilot/transitions.test.ts index ca7cfea2eab..a44011de446 100644 --- a/libs/sdk-ui-gen-ai/src/copilot/transitions.test.ts +++ b/libs/sdk-ui-gen-ai/src/copilot/transitions.test.ts @@ -10,7 +10,12 @@ import { import { selectCopilotGenerationPending, selectCopilotSource, selectCopilotStatus } from "./selectors.js"; import { createCopilotState, transitionCopilot } from "./transitions.js"; -import { type CopilotInput, type ICopilotConfiguration, type ICopilotSnapshot } from "./types.js"; +import { + CopilotBuildType, + type CopilotInput, + type ICopilotConfiguration, + type ICopilotSnapshot, +} from "./types.js"; enum Build { Board = "board", @@ -182,6 +187,59 @@ describe("pure copilot lifecycle", () => { next, ); }); + + it.each([false, true])("ignores old equal-ID events after navigation to a builder: %s", (isBuilder) => { + let state = initial(); + for (const [objectId, threadId] of [ + ["new-object", "new-thread"], + ["old-object", "old-thread"], + ]) { + state = transition(state, { + kind: "context", + context: { type: Build.Board, objectId, isEditing: true }, + }); + state = transition(state, event(user(threadId, threadId))); + state = transition( + state, + event({ + type: "chatAssistantMessage", + threadId, + useCase: "GENERAL", + skills: [{ name: "dashboard_editor", title: "Edit" }], + }), + ); + state = transition(state, event({ type: "chatReasoning", threadId, title: "Planning" })); + } + const newRequest = state.requests[0]; + const build = state.interaction.build; + state = transition(state, { + kind: "resetTarget", + target: { type: Build.Board, objectId: "old-object" }, + }); + state = transition(state, { + kind: "context", + context: isBuilder + ? { builderTarget: { type: Build.Board, objectId: "new-object" } } + : { type: Build.Board, objectId: "new-object", isEditing: true }, + }); + expect(state.requests).toEqual([newRequest]); + expect(state.requests[0]).toBe(newRequest); + expect(state.interaction.conversationId).toBe("old-thread"); + expect(state.interaction.threadId).toBeUndefined(); + expect(state.interaction.reasoningTitle).toBeUndefined(); + expect(state.interaction.build).toBe(build); + for (const value of [ + { type: "chatReasoning", threadId: "old-thread", title: "Late" }, + skill("old-thread"), + completed("old-thread"), + { type: "chatAssistantMessage", threadId: "old-thread", useCase: "INVALID" }, + { type: "chatAssistantMessage", threadId: "old-thread", useCase: "GENERAL", cancelled: true }, + questions("old-thread"), + ] satisfies ChatEvent[]) { + expect(transition(state, event(value))).toBe(state); + } + }); + it("transfers generation intent to a skill, waits, continues, and applies", () => { let state = started(); expect(selectCopilotStatus(state, scope)).toBe("generating"); @@ -322,22 +380,79 @@ describe("pure copilot lifecycle", () => { it("interruption preserves a still-running request until completion", () => { let state = transition(applying(), { kind: "applicationInterrupted", id: "application" }); expect(state.requests[0].applicationIds).toEqual([]); + expect(state.requests[0].responseFinished).toBeUndefined(); + expect(state.applications).toEqual([]); expect(selectCopilotStatus(state, scope)).toBe("building"); state = transition(state, event(completed())); expect(state.requests).toEqual([]); }); - it("matches an early failure by local ID or no ID", () => { + it.each([{ target }, { target, conversationId: "draft-local", threadId: "draft-thread" }])( + "clears an interrupted standalone application from $conversationId", + (source) => { + let state = transition(initial(), { kind: "applicationStarted", id: "standalone", source }); + expect(selectCopilotStatus(state, scope)).toBe("building"); + state = transition(state, { kind: "applicationInterrupted", id: "standalone" }); + expect(state.requests).toEqual([]); + expect(state.applications).toEqual([]); + expect(selectCopilotStatus(state, scope)).toBe("idle"); + }, + ); + + it.each([ + { type: "chatAssistantMessage", useCase: "INVALID" }, + { type: "chatAssistantMessage", useCase: "GENERAL", cancelled: true }, + ] satisfies ChatEvent[])("clears early generation after $useCase by local ID or no ID", (value) => { + const config: ICopilotConfiguration = { registrations: [] }; for (const conversationId of ["local", undefined]) { - let state = transition(initial(), event({ ...user(), conversationId, threadId: undefined })); - state = transition( + let state = transitionCopilot( + createCopilotState(), + { kind: "startGeneration", type: CopilotBuildType.Dashboard }, + config, + ); + state = transitionCopilot( state, - event({ type: "chatAssistantMessage", threadId: conversationId, useCase: "INVALID" }), + { kind: "event", event: { ...user(), conversationId, threadId: undefined } }, + config, + ); + state = transitionCopilot( + state, + { kind: "event", event: { ...value, threadId: conversationId } }, + config, ); expect(state.requests).toEqual([]); + expect(state.generation).toBeUndefined(); + expect(selectCopilotGenerationPending(state, CopilotBuildType.Dashboard)).toBe(false); + expect(selectCopilotStatus(state, { type: CopilotBuildType.Dashboard })).toBe("idle"); } }); + it.each([ + { type: "chatReasoning", threadId: "local", title: "Early" }, + skill("local"), + completed("local"), + questions("local"), + ] satisfies ChatEvent[])("does not accept local-ID $type as foreground before assignment", (value) => { + const state = transition( + transition(initial(), { kind: "startGeneration", type: Build.Board }), + event({ ...user(), threadId: undefined }), + ); + const next = transition(state, event(value)); + expect(next.interaction).toBe(state.interaction); + expect(next.generation).toBe(state.generation); + }); + + it.each([ + { type: "chatAssistantMessage", useCase: "INVALID" }, + { type: "chatAssistantMessage", useCase: "GENERAL", cancelled: true }, + ] satisfies ChatEvent[])("ignores stale local-ID $useCase after backend thread assignment", (value) => { + const state = activated(); + const next = transition(state, event({ ...value, threadId: "local" })); + expect(next.interaction).toBe(state.interaction); + expect(next.generation).toBe(state.generation); + expect(selectCopilotStatus(next, scope)).toBe("building"); + }); + it("keeps multiple applications locked and ignores duplicate and stale acknowledgements", () => { let state = applying(applying(), "newer"); state = transition(state, { kind: "applicationApplied", id: "application", target }); diff --git a/libs/sdk-ui-gen-ai/src/copilot/transitions.ts b/libs/sdk-ui-gen-ai/src/copilot/transitions.ts index 5adaea3b7be..4cc9a3d8e43 100644 --- a/libs/sdk-ui-gen-ai/src/copilot/transitions.ts +++ b/libs/sdk-ui-gen-ai/src/copilot/transitions.ts @@ -295,6 +295,15 @@ function isForegroundResponse( ): boolean { return ( foreground || + (event.type === "chatAssistantMessage" && + failedResponse(event) && + !state.interaction.threadId && + !!pending && + !pending.threadId && + !pending.responseFinished && + !!event.threadId && + event.threadId === state.interaction.conversationId && + pending.conversationId === state.interaction.conversationId) || (!event.threadId && !state.interaction.threadId && (!!pending || state.generation !== undefined || !!state.interaction.build)) @@ -432,6 +441,7 @@ function transitionApplicationStarted( objectId: source.target.objectId, conversationId: source.conversationId ?? pending?.conversationId, threadId: source.threadId ?? pending?.threadId, + responseFinished: pending ? pending.responseFinished : true, awaitingClarification: false, applicationIds: [...(pending?.applicationIds ?? []), input.id], }; diff --git a/libs/sdk-ui-gen-ai/src/copilot/types.ts b/libs/sdk-ui-gen-ai/src/copilot/types.ts index d02e0d5ee48..540509f8bff 100644 --- a/libs/sdk-ui-gen-ai/src/copilot/types.ts +++ b/libs/sdk-ui-gen-ai/src/copilot/types.ts @@ -10,6 +10,7 @@ import { /** Built-in copilot workflows. Applications may supply their own string enum. @alpha */ export enum CopilotBuildType { + /** Dashboard creation and editing. */ Dashboard = "dashboard", } @@ -98,7 +99,7 @@ export interface ICopilotRequest { readonly awaitingClarification?: boolean; /** Unanswered clarification item identifiers for this response. */ readonly pendingClarificationIds?: readonly string[]; - /** Response ended while its definitions were being applied. */ + /** No response remains in flight while definitions are being applied. */ readonly responseFinished?: boolean; /** In-progress host applications. */ readonly applicationIds: readonly string[]; diff --git a/libs/sdk-ui-gen-ai/src/index.ts b/libs/sdk-ui-gen-ai/src/index.ts index 65f9e791494..0baa977a5c7 100644 --- a/libs/sdk-ui-gen-ai/src/index.ts +++ b/libs/sdk-ui-gen-ai/src/index.ts @@ -8,6 +8,31 @@ * @beta */ +export { + CopilotHandler, + useCopilotSnapshot, + createCopilotState, + transitionCopilot, + selectCopilotGenerationPending, + selectCopilotStatus, + selectCopilotReasoningTitle, + selectCopilotSource, + CopilotBuildType, + type CopilotStatus, + type CopilotInput, + type ICopilotTarget, + type ICopilotContext, + type ICopilotScope, + type ICopilotSource, + type ICopilotApplicationSource, + type ICopilotInteraction, + type ICopilotRequest, + type ICopilotApplication, + type ICopilotSnapshot, + type ICopilotRegistration, + type ICopilotConfiguration, +} from "./copilot.js"; + export { GenAIChat, GenAIAssistant, diff --git a/libs/sdk-ui-geo/package.json b/libs/sdk-ui-geo/package.json index a59c0c305dc..2b94bb1113f 100644 --- a/libs/sdk-ui-geo/package.json +++ b/libs/sdk-ui-geo/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-ui-geo", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData.UI SDK - Geo Charts", "license": "LicenseRef-LICENSE", "author": "GoodData Corporation", diff --git a/libs/sdk-ui-kit/package.json b/libs/sdk-ui-kit/package.json index 65be37a5405..c717ee3b0b2 100644 --- a/libs/sdk-ui-kit/package.json +++ b/libs/sdk-ui-kit/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-ui-kit", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData SDK - UI Building Components", "license": "MIT", "author": "GoodData Corporation", diff --git a/libs/sdk-ui-loaders/package.json b/libs/sdk-ui-loaders/package.json index b8a47f9e8d9..d82f8d21700 100644 --- a/libs/sdk-ui-loaders/package.json +++ b/libs/sdk-ui-loaders/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-ui-loaders", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData SDK Runtime Component Loaders", "license": "LicenseRef-LICENSE", "author": "GoodData Corporation", diff --git a/libs/sdk-ui-metric-component/package.json b/libs/sdk-ui-metric-component/package.json index 9d605cd5b9c..ebd92bdd2ed 100644 --- a/libs/sdk-ui-metric-component/package.json +++ b/libs/sdk-ui-metric-component/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-ui-metric-component", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "license": "LicenseRef-LICENSE", "author": "GoodData Corporation", "repository": { diff --git a/libs/sdk-ui-pivot/package.json b/libs/sdk-ui-pivot/package.json index f05b388db7f..33ddd9d4743 100644 --- a/libs/sdk-ui-pivot/package.json +++ b/libs/sdk-ui-pivot/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-ui-pivot", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData.UI SDK - Pivot Table", "license": "LicenseRef-LICENSE", "author": "GoodData Corporation", diff --git a/libs/sdk-ui-pluggable-application/package.json b/libs/sdk-ui-pluggable-application/package.json index 08c242eb23b..8f630bfcde8 100644 --- a/libs/sdk-ui-pluggable-application/package.json +++ b/libs/sdk-ui-pluggable-application/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-ui-pluggable-application", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData SDK React helpers for pluggable applications", "license": "MIT", "author": "GoodData Corporation", diff --git a/libs/sdk-ui-pluggable-host/package.json b/libs/sdk-ui-pluggable-host/package.json index c6fcdcd909d..85d21901d6c 100644 --- a/libs/sdk-ui-pluggable-host/package.json +++ b/libs/sdk-ui-pluggable-host/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-ui-pluggable-host", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData SDK runtime for hosting pluggable applications — registry, loader, routing, platform context, default UI chrome", "license": "MIT", "author": "GoodData Corporation", diff --git a/libs/sdk-ui-semantic-search/package.json b/libs/sdk-ui-semantic-search/package.json index 82c931b5a0d..c22c0ad776b 100644 --- a/libs/sdk-ui-semantic-search/package.json +++ b/libs/sdk-ui-semantic-search/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-ui-semantic-search", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData SDK TypeScript & React skeleton", "license": "MIT", "author": "GoodData Corporation", diff --git a/libs/sdk-ui-tests-app/package.json b/libs/sdk-ui-tests-app/package.json index 0e1b7fa2554..8821a57806c 100644 --- a/libs/sdk-ui-tests-app/package.json +++ b/libs/sdk-ui-tests-app/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-ui-tests-app", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "private": true, "description": "GoodData SDK e2e tests", "license": "LicenseRef-LICENSE", diff --git a/libs/sdk-ui-tests-e2e/package.json b/libs/sdk-ui-tests-e2e/package.json index 85a707f4dae..d8a13386d6e 100644 --- a/libs/sdk-ui-tests-e2e/package.json +++ b/libs/sdk-ui-tests-e2e/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-ui-tests-e2e", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "private": true, "description": "GoodData SDK e2e tests", "license": "LicenseRef-LICENSE", diff --git a/libs/sdk-ui-tests-reference-workspace/package.json b/libs/sdk-ui-tests-reference-workspace/package.json index 35cccf83648..1c726120531 100644 --- a/libs/sdk-ui-tests-reference-workspace/package.json +++ b/libs/sdk-ui-tests-reference-workspace/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-ui-tests-reference-workspace", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "private": true, "description": "GoodData SDK reference workspace", "license": "LicenseRef-LICENSE", diff --git a/libs/sdk-ui-tests-scenarios/package.json b/libs/sdk-ui-tests-scenarios/package.json index da3c2acf4cb..7af513b8b96 100644 --- a/libs/sdk-ui-tests-scenarios/package.json +++ b/libs/sdk-ui-tests-scenarios/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-ui-tests-scenarios", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData React components scenarios project", "license": "LicenseRef-LICENSE", "author": "GoodData Corporation", diff --git a/libs/sdk-ui-tests-storybook/package.json b/libs/sdk-ui-tests-storybook/package.json index 633432d6daf..acbceb497b5 100644 --- a/libs/sdk-ui-tests-storybook/package.json +++ b/libs/sdk-ui-tests-storybook/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-ui-tests-storybook", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData React components storybook", "license": "LicenseRef-LICENSE", "author": "GoodData Corporation", diff --git a/libs/sdk-ui-theme-provider/package.json b/libs/sdk-ui-theme-provider/package.json index c063ab71a2f..3ce718b89cc 100644 --- a/libs/sdk-ui-theme-provider/package.json +++ b/libs/sdk-ui-theme-provider/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-ui-theme-provider", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData SDK - Theme provider", "license": "MIT", "author": "GoodData Corporation", diff --git a/libs/sdk-ui-vis-commons/package.json b/libs/sdk-ui-vis-commons/package.json index 76dfa25aa34..94832d290f5 100644 --- a/libs/sdk-ui-vis-commons/package.json +++ b/libs/sdk-ui-vis-commons/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-ui-vis-commons", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData.UI SDK - common functionality for different types of visualizations", "license": "MIT", "author": "GoodData Corporation", diff --git a/libs/sdk-ui-web-components/package.json b/libs/sdk-ui-web-components/package.json index 8d71e2f4807..10ae852ea36 100644 --- a/libs/sdk-ui-web-components/package.json +++ b/libs/sdk-ui-web-components/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-ui-web-components", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData Web Components wrapper for GoodData.UI", "license": "LicenseRef-LICENSE", "author": "GoodData Corporation", diff --git a/libs/sdk-ui/package.json b/libs/sdk-ui/package.json index 8b42051310d..533d2f7fa43 100644 --- a/libs/sdk-ui/package.json +++ b/libs/sdk-ui/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/sdk-ui", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData.UI SDK - Core", "license": "MIT", "author": "GoodData Corporation", diff --git a/libs/util/package.json b/libs/util/package.json index 13bbbfd41fa..350b4a17898 100644 --- a/libs/util/package.json +++ b/libs/util/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/util", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData Utility Functions", "license": "MIT", "author": "GoodData", diff --git a/templates/pluggable-module/harness/package.json b/templates/pluggable-module/harness/package.json index 1c062b18c42..9bc6d6efac9 100644 --- a/templates/pluggable-module/harness/package.json +++ b/templates/pluggable-module/harness/package.json @@ -1,6 +1,6 @@ { "name": "gdc-app-template-name-harness", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "private": true, "description": "Standalone harness for gdc-app-template-name module", "license": "UNLICENSED", diff --git a/templates/pluggable-module/module/package.json b/templates/pluggable-module/module/package.json index bf4112ea5b1..eb6f2d7127b 100644 --- a/templates/pluggable-module/module/package.json +++ b/templates/pluggable-module/module/package.json @@ -1,6 +1,6 @@ { "name": "gdc-app-template-name-module", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "private": true, "description": "{applicationTemplateTitle} pluggable application module", "license": "UNLICENSED", diff --git a/tools/app-toolkit/package.json b/tools/app-toolkit/package.json index ea0940a9890..a31c9e1ce70 100644 --- a/tools/app-toolkit/package.json +++ b/tools/app-toolkit/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/app-toolkit", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "CLI with useful tools for creating and maintaining GoodData web applications.", "license": "LicenseRef-LICENSE", "author": "GoodData", diff --git a/tools/catalog-export/package.json b/tools/catalog-export/package.json index 86a2d1b850c..7efd53cb3e4 100644 --- a/tools/catalog-export/package.json +++ b/tools/catalog-export/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/catalog-export", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData SDK Catalog Export tooling", "license": "MIT", "author": "GoodData", diff --git a/tools/create-pluggable-module/package.json b/tools/create-pluggable-module/package.json index 34cd901707d..9ba2e8fd141 100644 --- a/tools/create-pluggable-module/package.json +++ b/tools/create-pluggable-module/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/create-pluggable-module", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "Scaffolder for GoodData pluggable applications. Invoked via `npm init @gooddata/pluggable-module`.", "license": "MIT", "author": "GoodData Corporation", diff --git a/tools/dashboard-plugin-template/package.json b/tools/dashboard-plugin-template/package.json index 7640ab30c9a..3fd9880504b 100644 --- a/tools/dashboard-plugin-template/package.json +++ b/tools/dashboard-plugin-template/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/dashboard-plugin-template", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "private": true, "description": "GoodData Template for Dashboard Plugins", "license": "LicenseRef-LICENSE", diff --git a/tools/eslint-config/package.json b/tools/eslint-config/package.json index e88af09d1ea..1dd029fca3a 100644 --- a/tools/eslint-config/package.json +++ b/tools/eslint-config/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/eslint-config", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "license": "MIT", "author": "GoodData", "repository": { diff --git a/tools/i18n-toolkit/package.json b/tools/i18n-toolkit/package.json index dd694a2c2fb..ba35c78fe74 100644 --- a/tools/i18n-toolkit/package.json +++ b/tools/i18n-toolkit/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/i18n-toolkit", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "Localization validator to validate localization complexity and intl and html format.", "license": "MIT", "author": "GoodData", diff --git a/tools/lint-config/package.json b/tools/lint-config/package.json index ae14a1b0a6a..63f1fcf0d32 100644 --- a/tools/lint-config/package.json +++ b/tools/lint-config/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/lint-config", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "Linter-agnostic shared rules for @gooddata/eslint-config and @gooddata/oxlint-config", "license": "MIT", "author": "GoodData", diff --git a/tools/mock-handling/package.json b/tools/mock-handling/package.json index e2292638a4b..9bd19c1ad5f 100644 --- a/tools/mock-handling/package.json +++ b/tools/mock-handling/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/mock-handling", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData SDK Mock data capture and management tool", "license": "MIT", "author": "GoodData", diff --git a/tools/oxlint-config/package.json b/tools/oxlint-config/package.json index 516409ca4c4..65cb61ddd9b 100644 --- a/tools/oxlint-config/package.json +++ b/tools/oxlint-config/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/oxlint-config", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "license": "MIT", "author": "GoodData", "repository": { diff --git a/tools/plugin-toolkit/package.json b/tools/plugin-toolkit/package.json index 91e7fbbd7d0..6271f271f6d 100644 --- a/tools/plugin-toolkit/package.json +++ b/tools/plugin-toolkit/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/plugin-toolkit", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData Set of Tools for working with Plugins", "license": "LicenseRef-LICENSE", "author": "GoodData", diff --git a/tools/react-app-template/package.json b/tools/react-app-template/package.json index 55853e0dc83..abdbf68d8e5 100644 --- a/tools/react-app-template/package.json +++ b/tools/react-app-template/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/react-app-template", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "private": true, "description": "GoodData Template for React App", "license": "LicenseRef-LICENSE", diff --git a/tools/reference-workspace-mgmt/package.json b/tools/reference-workspace-mgmt/package.json index e56f56a5810..44389f7ffbc 100644 --- a/tools/reference-workspace-mgmt/package.json +++ b/tools/reference-workspace-mgmt/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/reference-workspace-mgmt", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "Command line tools for reference workspace management", "license": "MIT", "author": "GoodData", diff --git a/tools/reference-workspace/package.json b/tools/reference-workspace/package.json index f3b82f85606..2983bbceddb 100644 --- a/tools/reference-workspace/package.json +++ b/tools/reference-workspace/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/reference-workspace", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData SDK - Reference Workspace for tests", "license": "MIT", "author": "GoodData", diff --git a/tools/stylelint-config/package.json b/tools/stylelint-config/package.json index abf81027570..07a6ecfa47e 100644 --- a/tools/stylelint-config/package.json +++ b/tools/stylelint-config/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/stylelint-config", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "GoodData CSS Style Guide", "keywords": [ "config", diff --git a/tools/toolchain-config/package.json b/tools/toolchain-config/package.json index f883bdba8f9..5a355bba7b8 100644 --- a/tools/toolchain-config/package.json +++ b/tools/toolchain-config/package.json @@ -1,6 +1,6 @@ { "name": "@gooddata/toolchain-config", - "version": "11.66.0-alpha.0", + "version": "11.67.0-alpha.1", "description": "Shared build and test toolchain configuration (rolldown, vite, vitest, playwright) for gdc-ui packages", "license": "MIT", "author": "GoodData",