@gooddata/sdk-ui-ext 11.57.0-alpha.6 → 11.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/insightPicker/InsightPickerCore.d.ts.map +1 -1
- package/esm/insightPicker/InsightPickerCore.js +47 -16
- package/esm/insightPicker/useInsightPickerHybridSearch.d.ts +1 -1
- package/esm/insightPicker/useInsightPickerHybridSearch.d.ts.map +1 -1
- package/esm/insightPicker/useInsightPickerHybridSearch.js +10 -3
- package/esm/internal/components/insightList/useInsightPagedList.d.ts +8 -1
- package/esm/internal/components/insightList/useInsightPagedList.d.ts.map +1 -1
- package/esm/internal/components/insightList/useInsightPagedList.js +19 -12
- package/esm/internal/components/pluggableVisualizations/pivotTableNext/PluggablePivotTableNext.d.ts +1 -0
- package/esm/internal/components/pluggableVisualizations/pivotTableNext/PluggablePivotTableNext.d.ts.map +1 -1
- package/esm/internal/components/pluggableVisualizations/pivotTableNext/PluggablePivotTableNext.js +64 -11
- package/esm/internal/components/pluggableVisualizations/pivotTableNext/totalsHelpers.d.ts +18 -1
- package/esm/internal/components/pluggableVisualizations/pivotTableNext/totalsHelpers.d.ts.map +1 -1
- package/esm/internal/components/pluggableVisualizations/pivotTableNext/totalsHelpers.js +51 -2
- package/esm/internal/constants/supportedProperties.d.ts.map +1 -1
- package/esm/internal/constants/supportedProperties.js +1 -0
- package/esm/internal/utils/propertiesHelper.d.ts +10 -1
- package/esm/internal/utils/propertiesHelper.d.ts.map +1 -1
- package/esm/internal/utils/propertiesHelper.js +7 -0
- package/esm/sdk-ui-ext.d.ts +14 -6
- package/esm/share/accessErrors.d.ts +6 -5
- package/esm/share/accessErrors.d.ts.map +1 -1
- package/esm/share/accessErrors.js +7 -6
- package/esm/share/useLabelScope.js +4 -4
- package/package.json +21 -21
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InsightPickerCore.d.ts","sourceRoot":"","sources":["../../src/insightPicker/InsightPickerCore.tsx"],"names":[],"mappings":"AAkBA,OAAO,EAA2B,KAAK,mBAAmB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"InsightPickerCore.d.ts","sourceRoot":"","sources":["../../src/insightPicker/InsightPickerCore.tsx"],"names":[],"mappings":"AAkBA,OAAO,EAA2B,KAAK,mBAAmB,EAAE,MAAM,YAAY,CAAC;AA4B/E,wBAAgB,iBAAiB,CAAC,EAC9B,OAAO,EAAE,WAAW,EACpB,SAAS,EAAE,aAAa,EACxB,WAAW,EACX,WAAW,EACX,MAAM,EACN,+BAAoC,EACpC,iBAAiB,EACjB,gBAAgB,EAChB,WAAW,EACX,cAAc,EACd,MAAM,EACN,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,oBAAoB,EACpB,sBAAsB,EACtB,SAAS,EACT,iBAAiB,EACjB,oBAA2B,EAC3B,SAAe,EACf,KAAW,EACX,QAAQ,EACR,sBAAsB,EACtB,cAAc,EACd,WAAW,EACX,UAAU,EACV,UAAU,EAAE,cAAc,GAC7B,EAAE,mBAAmB,2CAwRrB"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
// (C) 2026 GoodData Corporation
|
|
3
|
-
import { useCallback, useEffect, useMemo, useRef } from "react";
|
|
3
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
4
4
|
import { useIntl } from "react-intl";
|
|
5
5
|
import { idRef, insightId } from "@gooddata/sdk-model";
|
|
6
6
|
import { useBackendStrict, useWorkspaceStrict } from "@gooddata/sdk-ui";
|
|
@@ -13,11 +13,30 @@ import { useInsightPickerFilters } from "./useInsightPickerFilters.js";
|
|
|
13
13
|
import { useInsightPickerHybridSearch } from "./useInsightPickerHybridSearch.js";
|
|
14
14
|
// These tab identifiers are internal keys for useInsightPagedList state — not i18n IDs.
|
|
15
15
|
const tabsIds = { my: "my", all: "all" };
|
|
16
|
+
const SEARCH_DEBOUNCE_MS = 300;
|
|
17
|
+
const EMPTY_RELATED_ITEMS = [];
|
|
18
|
+
// Clearing the field applies at once — waiting out a debounce to show the full list again reads
|
|
19
|
+
// as the picker being stuck.
|
|
20
|
+
function useDebouncedSearchQuery(searchQuery, delayMs) {
|
|
21
|
+
const [debouncedQuery, setDebouncedQuery] = useState(searchQuery);
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (searchQuery === "") {
|
|
24
|
+
setDebouncedQuery("");
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
const timeout = setTimeout(() => setDebouncedQuery(searchQuery), delayMs);
|
|
28
|
+
return () => clearTimeout(timeout);
|
|
29
|
+
}, [searchQuery, delayMs]);
|
|
30
|
+
return debouncedQuery;
|
|
31
|
+
}
|
|
16
32
|
export function InsightPickerCore({ backend: backendProp, workspace: workspaceProp, includeTags, excludeTags, author, enabledVisualizationClassesUrls = [], selectedInsightId, metadataTimeZone, searchQuery, onSearchChange, sortBy, sortDirection, onSortChange, authorFilter, onAuthorFilterChange, isAuthorFilterModified, tagFilter, onTagFilterChange, enableSemanticSearch = true, maxHeight = 350, width = 700, onSelect, onDescriptionPanelOpen, onItemActivate, menuActions, renderMenu, renderItem: renderItemProp, }) {
|
|
17
33
|
const intl = useIntl();
|
|
18
34
|
const backend = useBackendStrict(backendProp);
|
|
19
35
|
const workspace = useWorkspaceStrict(workspaceProp);
|
|
20
36
|
const hasMenu = !!(menuActions?.length || renderMenu);
|
|
37
|
+
// The list is paged, so only the server can answer a search over every visualization. The
|
|
38
|
+
// debounce keeps one query per pause in typing rather than one per keystroke.
|
|
39
|
+
const debouncedSearchQuery = useDebouncedSearchQuery(searchQuery, SEARCH_DEBOUNCE_MS);
|
|
21
40
|
// --- Filter options from API ---
|
|
22
41
|
const { authorOptions: authorFilterOptions, tagOptions: tagFilterOptions, authorsLoaded, tagsLoaded, } = useInsightPickerFilters(backend, workspace, author);
|
|
23
42
|
// --- Effective tags for the query (combine user selection with URL scope) ---
|
|
@@ -39,6 +58,7 @@ export function InsightPickerCore({ backend: backendProp, workspace: workspacePr
|
|
|
39
58
|
sortDirection,
|
|
40
59
|
createdByFilter: authorFilter.length > 0 ? authorFilter : undefined,
|
|
41
60
|
includeAuthorInfo: true,
|
|
61
|
+
searchTerm: debouncedSearchQuery,
|
|
42
62
|
});
|
|
43
63
|
// Fetch on mount
|
|
44
64
|
useEffect(() => {
|
|
@@ -48,8 +68,9 @@ export function InsightPickerCore({ backend: backendProp, workspace: workspacePr
|
|
|
48
68
|
const sortingKey = `${sortBy ?? ""},${sortDirection}`;
|
|
49
69
|
const authorFilterKey = authorFilter.join(",");
|
|
50
70
|
const tagFilterKey = tagFilter.join(",");
|
|
71
|
+
const searchKey = debouncedSearchQuery;
|
|
51
72
|
// Holds the filters the loaded items belong to. The reload effect below advances it.
|
|
52
|
-
const prevKeys = useRef({ sortingKey, authorFilterKey, tagFilterKey });
|
|
73
|
+
const prevKeys = useRef({ sortingKey, authorFilterKey, tagFilterKey, searchKey });
|
|
53
74
|
// The picker opens filtered to the current user. A user who authored nothing would face an
|
|
54
75
|
// empty picker, so drop that default and let the reload below fetch every author. The filter
|
|
55
76
|
// state must change, not just one query: every page is fetched from this state.
|
|
@@ -64,12 +85,13 @@ export function InsightPickerCore({ backend: backendProp, workspace: workspacePr
|
|
|
64
85
|
//
|
|
65
86
|
// This effect must stay ahead of the reload effect, which is what makes the last check work.
|
|
66
87
|
const isUntouchedAuthorDefault = !isAuthorFilterModified && !!author && authorFilter.length === 1 && authorFilter[0] === author;
|
|
67
|
-
const mayDropAuthorDefault = isUntouchedAuthorDefault && tagFilter.length === 0;
|
|
88
|
+
const mayDropAuthorDefault = isUntouchedAuthorDefault && tagFilter.length === 0 && searchKey === "";
|
|
68
89
|
useEffect(() => {
|
|
69
90
|
const prev = prevKeys.current;
|
|
70
91
|
const isCountStale = sortingKey !== prev.sortingKey ||
|
|
71
92
|
authorFilterKey !== prev.authorFilterKey ||
|
|
72
|
-
tagFilterKey !== prev.tagFilterKey
|
|
93
|
+
tagFilterKey !== prev.tagFilterKey ||
|
|
94
|
+
searchKey !== prev.searchKey;
|
|
73
95
|
if (!isCountStale && initialLoadCompleted && totalInsightsCount === 0 && mayDropAuthorDefault) {
|
|
74
96
|
onAuthorFilterChange([]);
|
|
75
97
|
}
|
|
@@ -81,21 +103,23 @@ export function InsightPickerCore({ backend: backendProp, workspace: workspacePr
|
|
|
81
103
|
sortingKey,
|
|
82
104
|
authorFilterKey,
|
|
83
105
|
tagFilterKey,
|
|
106
|
+
searchKey,
|
|
84
107
|
]);
|
|
85
|
-
// Reload when sorting or
|
|
108
|
+
// Reload when sorting, filters or the search term change (after initial load)
|
|
86
109
|
useEffect(() => {
|
|
87
110
|
const prev = prevKeys.current;
|
|
88
111
|
if (sortingKey !== prev.sortingKey ||
|
|
89
112
|
authorFilterKey !== prev.authorFilterKey ||
|
|
90
|
-
tagFilterKey !== prev.tagFilterKey
|
|
91
|
-
|
|
113
|
+
tagFilterKey !== prev.tagFilterKey ||
|
|
114
|
+
searchKey !== prev.searchKey) {
|
|
115
|
+
prevKeys.current = { sortingKey, authorFilterKey, tagFilterKey, searchKey };
|
|
92
116
|
resetItems();
|
|
93
117
|
}
|
|
94
|
-
}, [sortingKey, authorFilterKey, tagFilterKey, resetItems]);
|
|
118
|
+
}, [sortingKey, authorFilterKey, tagFilterKey, searchKey, resetItems]);
|
|
95
119
|
// --- Insight lookup ---
|
|
96
120
|
const insightsByIdentifier = useMemo(() => new Map(insights.map((insight) => [insightId(insight), insight])), [insights]);
|
|
97
121
|
// --- Hybrid search ---
|
|
98
|
-
const { searchState, semanticSearchState,
|
|
122
|
+
const { searchState, semanticSearchState, searchEntries, relatedItems, isSearching, handleSearchChange } = useInsightPickerHybridSearch({
|
|
99
123
|
insights,
|
|
100
124
|
searchQuery,
|
|
101
125
|
onSearchChange,
|
|
@@ -103,7 +127,14 @@ export function InsightPickerCore({ backend: backendProp, workspace: workspacePr
|
|
|
103
127
|
includeTags,
|
|
104
128
|
excludeTags,
|
|
105
129
|
});
|
|
106
|
-
|
|
130
|
+
// Semantic hits are appended below the literal matches, so they may only be shown once every
|
|
131
|
+
// page of those matches is in. Otherwise the next page would push them further down the list.
|
|
132
|
+
// A reload reports no next page until the first page of the new query lands, so the loading
|
|
133
|
+
// flags and the debounce have to be checked too — `hasNextPage` alone is false in that window.
|
|
134
|
+
const isLiteralSearchComplete = searchState.query === debouncedSearchQuery && !isLoading && !isNextPageLoading && !hasNextPage;
|
|
135
|
+
const appendedRelatedItems = isSearching && isLiteralSearchComplete ? relatedItems : EMPTY_RELATED_ITEMS;
|
|
136
|
+
const displayItems = useMemo(() => [...searchEntries, ...appendedRelatedItems], [searchEntries, appendedRelatedItems]);
|
|
137
|
+
const totalItems = (totalInsightsCount ?? insights.length) + appendedRelatedItems.length;
|
|
107
138
|
const shouldLoadNextPage = useCallback((lastItemIndex, itemsCount) => lastItemIndex >= itemsCount - 5, []);
|
|
108
139
|
const handleItemClick = useCallback((entry) => onSelect(idRef(entry.identifier, "insight"), entry), [onSelect]);
|
|
109
140
|
return (_jsxs("div", { className: "gd-ui-ext-insight-picker", children: [initialLoadCompleted ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "gd-ui-ext-insight-picker-search", onKeyDown: (e) => {
|
|
@@ -112,11 +143,11 @@ export function InsightPickerCore({ backend: backendProp, workspace: workspacePr
|
|
|
112
143
|
handleSearchChange("");
|
|
113
144
|
}
|
|
114
145
|
// When search is empty, let Esc propagate to close the dialog
|
|
115
|
-
}, children: _jsx(Input, { isSearch: true, autofocus: true, placeholder: intl.formatMessage(messages.searchPlaceholder), value: searchState.query, onChange: (value) => handleSearchChange(String(value)) }) }), _jsx(InsightPickerFilterBar, { author: author, authorFilterOptions: authorFilterOptions, tagFilterOptions: tagFilterOptions, authorsLoaded: authorsLoaded, tagsLoaded: tagsLoaded, includeTags: includeTags, excludeTags: excludeTags, authorFilter: authorFilter, tagFilter: tagFilter, onAuthorFilterChange: onAuthorFilterChange, onTagFilterChange: onTagFilterChange, sortBy: sortBy, sortDirection: sortDirection, onSortChange: onSortChange })] })) : null, _jsx(DropdownList, { width: width, isMobile: false, isLoading:
|
|
116
|
-
?
|
|
117
|
-
:
|
|
118
|
-
?
|
|
119
|
-
: undefined, showSearch: false, onKeyDownConfirm: (entry) => {
|
|
146
|
+
}, children: _jsx(Input, { isSearch: true, autofocus: true, placeholder: intl.formatMessage(messages.searchPlaceholder), value: searchState.query, onChange: (value) => handleSearchChange(String(value)) }) }), _jsx(InsightPickerFilterBar, { author: author, authorFilterOptions: authorFilterOptions, tagFilterOptions: tagFilterOptions, authorsLoaded: authorsLoaded, tagsLoaded: tagsLoaded, includeTags: includeTags, excludeTags: excludeTags, authorFilter: authorFilter, tagFilter: tagFilter, onAuthorFilterChange: onAuthorFilterChange, onTagFilterChange: onTagFilterChange, sortBy: sortBy, sortDirection: sortDirection, onSortChange: onSortChange })] })) : null, _jsx(DropdownList, { width: width, isMobile: false, isLoading: isLoading
|
|
147
|
+
? insights.length === 0
|
|
148
|
+
: isSearching && semanticSearchState.state === "loading"
|
|
149
|
+
? displayItems.length === 0
|
|
150
|
+
: undefined, showSearch: false, searchString: searchState.query, onKeyDownConfirm: (entry) => {
|
|
120
151
|
if (onItemActivate) {
|
|
121
152
|
const sourceInsight = insightsByIdentifier.get(entry.identifier);
|
|
122
153
|
onItemActivate(entry, sourceInsight);
|
|
@@ -124,7 +155,7 @@ export function InsightPickerCore({ backend: backendProp, workspace: workspacePr
|
|
|
124
155
|
else {
|
|
125
156
|
handleItemClick(entry);
|
|
126
157
|
}
|
|
127
|
-
}, itemHeight: 50, items: displayItems, itemsCount: totalItems, maxHeight: maxHeight, loadNextPage:
|
|
158
|
+
}, itemHeight: 50, items: displayItems, itemsCount: totalItems, maxHeight: maxHeight, loadNextPage: loadNextPage, hasNextPage: hasNextPage, skeletonItemsCount: skeletonItemsCount, isNextPageLoading: isNextPageLoading, shouldLoadNextPage: shouldLoadNextPage, SkeletonItem: () => (_jsx(UiSkeleton, { itemWidth: ["100%"], direction: "row", itemPadding: 15, itemHeight: 30, itemsCount: 1, itemsGap: 0 })), renderItem: ({ item: entry, width: itemWidth }) => {
|
|
128
159
|
const type = (enabledVisualizationClassesUrls.includes(entry.visualizationUrl) &&
|
|
129
160
|
entry.visualizationUrl?.split(":")[1]) ||
|
|
130
161
|
"unknown";
|
|
@@ -17,7 +17,7 @@ export declare function useInsightPickerHybridSearch({ insights, searchQuery, on
|
|
|
17
17
|
searchState: import("@gooddata/sdk-ui-semantic-search").ISearchState;
|
|
18
18
|
semanticSearchState: import("@gooddata/sdk-ui-semantic-search").ISemanticSearchState;
|
|
19
19
|
searchEntries: IInsightPickerItem[];
|
|
20
|
-
|
|
20
|
+
relatedItems: IInsightPickerItem[];
|
|
21
21
|
isSearching: boolean;
|
|
22
22
|
handleSearchChange: (query: string) => void;
|
|
23
23
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useInsightPickerHybridSearch.d.ts","sourceRoot":"","sources":["../../src/insightPicker/useInsightPickerHybridSearch.ts"],"names":[],"mappings":"AAIA,OAAO,EACH,KAAK,QAAQ,EAWhB,MAAM,qBAAqB,CAAC;AAQ7B,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAsBrD,UAAU,oCAAoC;IAC1C,QAAQ,EAAE,QAAQ,EAAE,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,EACzC,QAAQ,EACR,WAAW,EACX,cAAc,EACd,oBAA2B,EAC3B,WAAW,EACX,WAAW,GACd,EAAE,oCAAoC;;;;;;gCA8CvB,MAAM;
|
|
1
|
+
{"version":3,"file":"useInsightPickerHybridSearch.d.ts","sourceRoot":"","sources":["../../src/insightPicker/useInsightPickerHybridSearch.ts"],"names":[],"mappings":"AAIA,OAAO,EACH,KAAK,QAAQ,EAWhB,MAAM,qBAAqB,CAAC;AAQ7B,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAsBrD,UAAU,oCAAoC;IAC1C,QAAQ,EAAE,QAAQ,EAAE,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,EACzC,QAAQ,EACR,WAAW,EACX,cAAc,EACd,oBAA2B,EAC3B,WAAW,EACX,WAAW,GACd,EAAE,oCAAoC;;;;;;gCA8CvB,MAAM;EAoCrB"}
|
|
@@ -52,13 +52,20 @@ export function useInsightPickerHybridSearch({ insights, searchQuery, onSearchCh
|
|
|
52
52
|
onSearchChange(query);
|
|
53
53
|
}, [setHybridSearchQuery, onSearchChange]);
|
|
54
54
|
const isSearching = searchState.query !== "";
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
// The loaded entries are the server's own matches, so only the semantic hits are taken from
|
|
56
|
+
// here. Re-matching the entries on the client would drop the ones the server matched on a
|
|
57
|
+
// field this matcher does not read, leaving rows missing from a list whose count still counts
|
|
58
|
+
// them.
|
|
59
|
+
const { searchRelatedItems } = useMemo(() => hybridSearch({ items: searchEntries }), [hybridSearch, searchEntries]);
|
|
60
|
+
const loadedIdentifiers = useMemo(() => new Set(searchEntries.map((entry) => entry.identifier)), [searchEntries]);
|
|
61
|
+
// Semantic hits carry identifier refs while loaded insights carry URI refs, so the generic
|
|
62
|
+
// ObjRef comparison upstream never sees them as the same object. Compare identifiers instead.
|
|
63
|
+
const relatedItems = useMemo(() => searchRelatedItems.filter((item) => !loadedIdentifiers.has(item.identifier)), [searchRelatedItems, loadedIdentifiers]);
|
|
57
64
|
return {
|
|
58
65
|
searchState,
|
|
59
66
|
semanticSearchState,
|
|
60
67
|
searchEntries,
|
|
61
|
-
|
|
68
|
+
relatedItems,
|
|
62
69
|
isSearching,
|
|
63
70
|
handleSearchChange,
|
|
64
71
|
};
|
|
@@ -50,6 +50,13 @@ export interface IUsePagedDropdownConfig {
|
|
|
50
50
|
* Only used by the enhanced insight picker — other consumers should leave this off.
|
|
51
51
|
*/
|
|
52
52
|
includeAuthorInfo?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Search term owned by the caller. It replaces the hook's own search state, so that every page
|
|
55
|
+
* the hook fetches is already narrowed by the server instead of by the caller afterwards. It is
|
|
56
|
+
* matched across title, identifier, description and tags, unlike the tab-based term, which the
|
|
57
|
+
* backend matches against the title alone.
|
|
58
|
+
*/
|
|
59
|
+
searchTerm?: string;
|
|
53
60
|
}
|
|
54
61
|
/**
|
|
55
62
|
* Result of useInsightPagedList hook
|
|
@@ -94,5 +101,5 @@ export interface IUsePagedDropdownResult {
|
|
|
94
101
|
*
|
|
95
102
|
* @internal
|
|
96
103
|
*/
|
|
97
|
-
export declare function useInsightPagedList({ backend, workspaceId, author, tabsIds, tags, excludeTags, sortBy, sortDirection, createdByFilter, includeAuthorInfo, }: IUsePagedDropdownConfig): IUsePagedDropdownResult;
|
|
104
|
+
export declare function useInsightPagedList({ backend, workspaceId, author, tabsIds, tags, excludeTags, sortBy, sortDirection, createdByFilter, includeAuthorInfo, searchTerm, }: IUsePagedDropdownConfig): IUsePagedDropdownResult;
|
|
98
105
|
//# sourceMappingURL=useInsightPagedList.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useInsightPagedList.d.ts","sourceRoot":"","sources":["../../../../src/internal/components/insightList/useInsightPagedList.tsx"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,kBAAkB,EAA2B,MAAM,2BAA2B,CAAC;AAC7F,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAIjD;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,cAAc,GAAG,MAAM,CAAC;AAExD;;;GAGG;AACH,MAAM,MAAM,wBAAwB,GAAG,KAAK,GAAG,MAAM,CAAC;AAEtD;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACpC,OAAO,EAAE,kBAAkB,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,OAAO,EAAE,QAAQ,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB;;;OAGG;IACH,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B;;OAEG;IACH,aAAa,CAAC,EAAE,wBAAwB,CAAC;IACzC;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"useInsightPagedList.d.ts","sourceRoot":"","sources":["../../../../src/internal/components/insightList/useInsightPagedList.tsx"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,kBAAkB,EAA2B,MAAM,2BAA2B,CAAC;AAC7F,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAIjD;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,cAAc,GAAG,MAAM,CAAC;AAExD;;;GAGG;AACH,MAAM,MAAM,wBAAwB,GAAG,KAAK,GAAG,MAAM,CAAC;AAEtD;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACpC,OAAO,EAAE,kBAAkB,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,OAAO,EAAE,QAAQ,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB;;;OAGG;IACH,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B;;OAEG;IACH,aAAa,CAAC,EAAE,wBAAwB,CAAC;IACzC;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACpC,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,SAAS,EAAE,OAAO,CAAC;IACnB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,oBAAoB,EAAE,OAAO,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,UAAU,EAAE,CAAC,MAAM,EAAE;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,CAAC,EAAE,OAAO,CAAC;KACxB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpB,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,QAAQ,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,IAAI,CAAC;IAClC,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,kBAAkB,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC;IAC3E,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,UAAU,EAAE,MAAM,IAAI,CAAC;CAC1B;AAWD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,EAChC,OAAO,EACP,WAAW,EACX,MAAM,EACN,OAAO,EACP,IAAI,EACJ,WAAW,EACX,MAAM,EACN,aAAsC,EACtC,eAAe,EACf,iBAAyB,EACzB,UAAU,GACb,EAAE,uBAAuB,GAAG,uBAAuB,CAsPnD"}
|
|
@@ -23,7 +23,7 @@ function buildSorting(sortBy, direction) {
|
|
|
23
23
|
*
|
|
24
24
|
* @internal
|
|
25
25
|
*/
|
|
26
|
-
export function useInsightPagedList({ backend, workspaceId, author, tabsIds, tags, excludeTags, sortBy, sortDirection = DEFAULT_SORT_DIRECTION, createdByFilter, includeAuthorInfo = false, }) {
|
|
26
|
+
export function useInsightPagedList({ backend, workspaceId, author, tabsIds, tags, excludeTags, sortBy, sortDirection = DEFAULT_SORT_DIRECTION, createdByFilter, includeAuthorInfo = false, searchTerm, }) {
|
|
27
27
|
const [items, setItems] = useState([]);
|
|
28
28
|
const [totalItemsCount, setTotalItemsCount] = useState(undefined);
|
|
29
29
|
const [isLoading, setIsLoading] = useState(false);
|
|
@@ -32,6 +32,8 @@ export function useInsightPagedList({ backend, workspaceId, author, tabsIds, tag
|
|
|
32
32
|
const [currentPage, setCurrentPage] = useState(0);
|
|
33
33
|
const [search, setSearch] = useState("");
|
|
34
34
|
const [selectedTabId, setSelectedTabId] = useState(tabsIds.my);
|
|
35
|
+
const effectiveSearch = searchTerm ?? search;
|
|
36
|
+
const ownsSearchTerm = searchTerm !== undefined;
|
|
35
37
|
const abortControllerRef = useRef(null);
|
|
36
38
|
const initialLoadCompletedRef = useRef(false);
|
|
37
39
|
const sorting = useMemo(() => buildSorting(sortBy, sortDirection), [sortBy, sortDirection]);
|
|
@@ -68,7 +70,7 @@ export function useInsightPagedList({ backend, workspaceId, author, tabsIds, tag
|
|
|
68
70
|
.withSize(ITEMS_PER_PAGE)
|
|
69
71
|
.withPage(page)
|
|
70
72
|
.withSorting(sorting);
|
|
71
|
-
const
|
|
73
|
+
const searchedText = searchValue || undefined;
|
|
72
74
|
// When includeAuthorInfo is true (picker mode), only use the explicit createdByFilter.
|
|
73
75
|
// Otherwise fall back to the legacy tab-based author filtering.
|
|
74
76
|
const searchedAuthor = !includeAuthorInfo && tabId === tabsIds.my && isEmpty(searchValue) ? author : undefined;
|
|
@@ -83,7 +85,11 @@ export function useInsightPagedList({ backend, workspaceId, author, tabsIds, tag
|
|
|
83
85
|
const filter = {
|
|
84
86
|
...((tags?.length ?? 0) > 0 ? { tags } : {}),
|
|
85
87
|
...((excludeTags?.length ?? 0) > 0 ? { excludeTags } : {}),
|
|
86
|
-
...(
|
|
88
|
+
...(searchedText
|
|
89
|
+
? ownsSearchTerm
|
|
90
|
+
? { search: searchedText }
|
|
91
|
+
: { title: searchedText }
|
|
92
|
+
: {}),
|
|
87
93
|
...(effectiveCreatedBy ? { createdBy: effectiveCreatedBy } : {}),
|
|
88
94
|
};
|
|
89
95
|
if (Object.keys(filter).length > 0) {
|
|
@@ -161,6 +167,7 @@ export function useInsightPagedList({ backend, workspaceId, author, tabsIds, tag
|
|
|
161
167
|
sorting,
|
|
162
168
|
createdByFilter,
|
|
163
169
|
includeAuthorInfo,
|
|
170
|
+
ownsSearchTerm,
|
|
164
171
|
]);
|
|
165
172
|
const loadNextPage = useCallback(() => {
|
|
166
173
|
if (!hasNextPage || isNextPageLoading || isLoading) {
|
|
@@ -168,8 +175,8 @@ export function useInsightPagedList({ backend, workspaceId, author, tabsIds, tag
|
|
|
168
175
|
}
|
|
169
176
|
const nextPage = currentPage + 1;
|
|
170
177
|
setCurrentPage(nextPage);
|
|
171
|
-
void fetchItems({ page: nextPage, search, tabId: selectedTabId });
|
|
172
|
-
}, [hasNextPage, isNextPageLoading, isLoading, currentPage,
|
|
178
|
+
void fetchItems({ page: nextPage, search: effectiveSearch, tabId: selectedTabId });
|
|
179
|
+
}, [hasNextPage, isNextPageLoading, isLoading, currentPage, effectiveSearch, selectedTabId, fetchItems]);
|
|
173
180
|
// oxlint-disable-next-line react-hooks/exhaustive-deps
|
|
174
181
|
const onSearch = useCallback(debounce((searchString) => {
|
|
175
182
|
setCurrentPage(0);
|
|
@@ -180,8 +187,8 @@ export function useInsightPagedList({ backend, workspaceId, author, tabsIds, tag
|
|
|
180
187
|
const { id: tabId } = tab;
|
|
181
188
|
setCurrentPage(0);
|
|
182
189
|
setSelectedTabId(tabId);
|
|
183
|
-
void fetchItems({ page: 0, search, tabId, resetItems: true });
|
|
184
|
-
}, [fetchItems,
|
|
190
|
+
void fetchItems({ page: 0, search: effectiveSearch, tabId, resetItems: true });
|
|
191
|
+
}, [fetchItems, effectiveSearch]);
|
|
185
192
|
const reset = useCallback(() => {
|
|
186
193
|
if (abortControllerRef.current) {
|
|
187
194
|
abortControllerRef.current.abort();
|
|
@@ -196,12 +203,12 @@ export function useInsightPagedList({ backend, workspaceId, author, tabsIds, tag
|
|
|
196
203
|
const shouldLoadNextPage = useCallback((lastItemIndex, itemsCount) => lastItemIndex >= itemsCount - 5, []);
|
|
197
204
|
const loadInitialItems = useCallback(() => {
|
|
198
205
|
setCurrentPage(0);
|
|
199
|
-
void fetchItems({ page: 0, search:
|
|
200
|
-
}, [fetchItems]);
|
|
206
|
+
void fetchItems({ page: 0, search: effectiveSearch, tabId: undefined, resetItems: true });
|
|
207
|
+
}, [fetchItems, effectiveSearch]);
|
|
201
208
|
const resetItems = useCallback(() => {
|
|
202
209
|
setCurrentPage(0);
|
|
203
|
-
void fetchItems({ page: 0, search:
|
|
204
|
-
}, [fetchItems,
|
|
210
|
+
void fetchItems({ page: 0, search: effectiveSearch, tabId: selectedTabId, resetItems: true });
|
|
211
|
+
}, [fetchItems, effectiveSearch, selectedTabId]);
|
|
205
212
|
return {
|
|
206
213
|
items,
|
|
207
214
|
totalItemsCount,
|
|
@@ -209,7 +216,7 @@ export function useInsightPagedList({ backend, workspaceId, author, tabsIds, tag
|
|
|
209
216
|
isNextPageLoading,
|
|
210
217
|
initialLoadCompleted,
|
|
211
218
|
currentPage,
|
|
212
|
-
search,
|
|
219
|
+
search: effectiveSearch,
|
|
213
220
|
selectedTabId,
|
|
214
221
|
hasNextPage,
|
|
215
222
|
skeletonItemsCount,
|
package/esm/internal/components/pluggableVisualizations/pivotTableNext/PluggablePivotTableNext.d.ts
CHANGED
|
@@ -39,6 +39,7 @@ export declare class PluggablePivotTableNext extends AbstractPluggableVisualizat
|
|
|
39
39
|
private adaptPropertiesToInsight;
|
|
40
40
|
private sanitizeColumnWidths;
|
|
41
41
|
private onColumnResized;
|
|
42
|
+
private persistTotalsOverride;
|
|
42
43
|
private handlePushData;
|
|
43
44
|
}
|
|
44
45
|
//# sourceMappingURL=PluggablePivotTableNext.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PluggablePivotTableNext.d.ts","sourceRoot":"","sources":["../../../../../src/internal/components/pluggableVisualizations/pivotTableNext/PluggablePivotTableNext.tsx"],"names":[],"mappings":"AAIA,OAAO,EAGH,KAAK,iBAAiB,EACzB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACH,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,kBAAkB,EACvB,KAAK,SAAS,
|
|
1
|
+
{"version":3,"file":"PluggablePivotTableNext.d.ts","sourceRoot":"","sources":["../../../../../src/internal/components/pluggableVisualizations/pivotTableNext/PluggablePivotTableNext.tsx"],"names":[],"mappings":"AAIA,OAAO,EAGH,KAAK,iBAAiB,EACzB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACH,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,kBAAkB,EACvB,KAAK,SAAS,EAcjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAIH,KAAK,wBAAwB,EAEhC,MAAM,kBAAkB,CAAC;AAM1B,OAAO,EAGH,KAAK,oBAAoB,EAE5B,MAAM,6BAA6B,CAAC;AAKrC,OAAO,EAEH,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAC5B,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,KAAK,wBAAwB,EAGhC,MAAM,sCAAsC,CAAC;AAqC9C,OAAO,EAAE,8BAA8B,EAAE,MAAM,sCAAsC,CAAC;AAiBtF,wBAAgB,0BAA0B,CACtC,MAAM,EAAE,UAAU,EAClB,WAAW,EAAE,wBAAwB,EACrC,QAAQ,EAAE,SAAS,GACpB,oBAAoB,CAmCtB;AAED;;;;;GAKG;AACH,qBAAa,uBAAwB,SAAQ,8BAA8B;IACvE,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,UAAU,CAAkB;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAY;IACrC,OAAO,CAAC,mBAAmB,CAAuB;IAElD,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,cAAc,CAAkB;IACxC,OAAO,CAAC,WAAW,CAAuC;IAE1D,OAAO,CAAC,YAAY,CAAqB;IAEzC,YAAY,KAAK,EAAE,aAAa,EAqB/B;IAEM,OAAO,IAAI,IAAI,CAErB;IAEM,yBAAyB,CAC5B,cAAc,EAAE,eAAe,EAC/B,sBAAsB,CAAC,EAAE,eAAe,GACzC,OAAO,CAAC,uBAAuB,CAAC,CA8GlC;IAEe,8BAA8B,CAC1C,mBAAmB,EAAE,QAAQ,EAC7B,gBAAgB,EAAE,iBAAiB,EACnC,0BAA0B,EAAE,OAAO,GACpC,QAAQ,CAWV;IAED,OAAO,CAAC,kBAAkB;IAInB,YAAY,CACf,OAAO,EAAE,SAAS,EAClB,OAAO,EAAE,kBAAkB,EAC3B,gBAAgB,EAAE,iBAAiB,0DActC;IAED,SAAS,CAAC,oBAAoB,CAAC,uBAAuB,EAAE,wBAAwB,GAAG,IAAI,CActF;IAKD,OAAO,CAAC,cAAc,CAIpB;IAIF,OAAO,CAAC,oBAAoB,CAe1B;IAEF,OAAO,CAAC,yBAAyB,CAe/B;IAEF,UAAmB,wBAAwB,CACvC,OAAO,EAAE,SAAS,EAClB,OAAO,EAAE,kBAAkB,EAC3B,qBAAqB,EAAE,GAAG,GAC3B,IAAI,CASN;IAED,UAAmB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAOzE;IAED,SAAS,CAAC,mBAAmB,CACzB,OAAO,EAAE,SAAS,EAClB,OAAO,EAAE,kBAAkB,EAC3B,gBAAgB,EAAE,iBAAiB,GACpC,IAAI,CA8FN;IAED,SAAS,CAAC,wBAAwB,CAAC,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,SAAS,GAAG,IAAI,CA6BxF;IAED,SAAS,CAAC,aAAa,CAAC,OAAO,EAAE,kBAAkB,EAAE,yBAAyB,CAAC,EAAE,GAAG,GAAG,UAAU,EAAE,CAElG;IAED,OAAO,CAAC,wBAAwB;IAYhC,OAAO,CAAC,oBAAoB;IA8B5B,OAAO,CAAC,eAAe;IAkBvB,OAAO,CAAC,qBAAqB;IAqB7B,OAAO,CAAC,cAAc;CA8CzB"}
|
package/esm/internal/components/pluggableVisualizations/pivotTableNext/PluggablePivotTableNext.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
// (C) 2025-2026 GoodData Corporation
|
|
3
3
|
import { cloneDeep, isEmpty, isEqual } from "lodash-es";
|
|
4
|
-
import { bucketAttributes, bucketMeasures, insightBucket, insightBuckets, insightFilters, insightHasDataDefined, insightProperties, insightSanitize, insightSorts, resolveWeekStart, } from "@gooddata/sdk-model";
|
|
4
|
+
import { bucketAttributes, bucketMeasures, bucketTotals, insightBucket, insightBuckets, insightFilters, insightHasDataDefined, insightProperties, insightSanitize, insightSorts, resolveWeekStart, } from "@gooddata/sdk-model";
|
|
5
5
|
import { BucketNames, VisualizationTypes, } from "@gooddata/sdk-ui";
|
|
6
6
|
import { CorePivotTableNext, } from "@gooddata/sdk-ui-pivot/next";
|
|
7
7
|
import { METRIC } from "../../../constants/bucket.js";
|
|
8
|
-
import { DASHBOARDS_ENVIRONMENT } from "../../../constants/properties.js";
|
|
8
|
+
import { ANALYTICAL_ENVIRONMENT, DASHBOARDS_ENVIRONMENT } from "../../../constants/properties.js";
|
|
9
9
|
import { PIVOT_TABLE_NEXT_SUPPORTED_PROPERTIES } from "../../../constants/supportedProperties.js";
|
|
10
10
|
import { configureOverTimeComparison, configurePercent } from "../../../utils/bucketConfig.js";
|
|
11
11
|
import { getAllItemsByType, getTotalsFromBucket, limitNumberOfMeasuresInBuckets, removeDuplicateBucketItems, sanitizeFilters, } from "../../../utils/bucketHelper.js";
|
|
12
12
|
import { isSetColumnHeadersPositionToLeftAllowed } from "../../../utils/controlsHelper.js";
|
|
13
13
|
import { generateDimensions } from "../../../utils/dimensions.js";
|
|
14
|
-
import { getColumnHeadersPositionFromProperties, getColumnWidthsFromProperties, getEffectiveConditionalFormatting, getGrandTotalsPositionFromProperties, getMeasureGroupDimensionFromProperties, getPageSizeFromProperties, getPaginationFromProperties, getPivotTableProperties, getReferencePointWithSupportedProperties, getSupportedPropertiesControls, getTextWrappingFromProperties, isSemanticConditionalFormattingEnabled, } from "../../../utils/propertiesHelper.js";
|
|
14
|
+
import { getColumnHeadersPositionFromProperties, getColumnWidthsFromProperties, getEffectiveConditionalFormatting, getGrandTotalsPositionFromProperties, getMeasureGroupDimensionFromProperties, getPageSizeFromProperties, getPaginationFromProperties, getPivotTableProperties, getReferencePointWithSupportedProperties, getSupportedPropertiesControls, getTextWrappingFromProperties, getTotalsOverrideFromProperties, isSemanticConditionalFormattingEnabled, } from "../../../utils/propertiesHelper.js";
|
|
15
15
|
import { getPivotTableNextDefaultUiConfig, getPivotTableNextMeasuresLimit, setPivotTableNextUiConfig, } from "../../../utils/uiConfigHelpers/pivotTableNextUiConfigHelper.js";
|
|
16
16
|
import { buildCfTargetData, } from "../../configurationControls/conditionalFormatting/conditionalFormattingModel.js";
|
|
17
17
|
import { PivotTableConfigurationPanel } from "../../configurationPanels/PivotTableConfigurationPanel.js";
|
|
@@ -19,7 +19,7 @@ import { AbstractPluggableVisualization } from "../AbstractPluggableVisualizatio
|
|
|
19
19
|
import { addIntersectionFiltersToInsight, modifyBucketsAttributesForDrillDown, sanitizeTableProperties, } from "../drillDownUtil.js";
|
|
20
20
|
import { getColumnAttributes, getRowAttributes, shouldAdjustColumnHeadersPositionToTop } from "./helpers.js";
|
|
21
21
|
import { adaptReferencePointSortItemsToPivotTable, getSanitizedSortItems } from "./sortHelpers.js";
|
|
22
|
-
import { removeInvalidTotals } from "./totalsHelpers.js";
|
|
22
|
+
import { applyTotalsOverride, getChangedTotals, removeInvalidTotals } from "./totalsHelpers.js";
|
|
23
23
|
import { adaptMdObjectWidthItemsToPivotTable, adaptReferencePointWidthItemsToPivotTable, } from "./widthItemsHelpers.js";
|
|
24
24
|
const PROPERTIES_AFFECTING_REFERENCE_POINT = ["measureGroupDimension"];
|
|
25
25
|
export function createPivotTableNextConfig(config, environment, settings) {
|
|
@@ -29,6 +29,10 @@ export function createPivotTableNextConfig(config, environment, settings) {
|
|
|
29
29
|
agGridToken: config.agGridToken,
|
|
30
30
|
enableAccessibility: settings.enableAccessibilityMode ?? false,
|
|
31
31
|
};
|
|
32
|
+
// Total labels are only ever editable in AD, or on a dashboard while it's in edit mode - "none"
|
|
33
|
+
// (embedded examples, which have nowhere to persist an edited label) is excluded naturally.
|
|
34
|
+
const totalLabelsEditable = environment === ANALYTICAL_ENVIRONMENT ||
|
|
35
|
+
(environment === DASHBOARDS_ENVIRONMENT && !!config.isInEditMode);
|
|
32
36
|
if (environment !== DASHBOARDS_ENVIRONMENT) {
|
|
33
37
|
tableConfig = {
|
|
34
38
|
...tableConfig,
|
|
@@ -36,6 +40,16 @@ export function createPivotTableNextConfig(config, environment, settings) {
|
|
|
36
40
|
aggregations: true,
|
|
37
41
|
aggregationsSubMenu: true,
|
|
38
42
|
aggregationsSubMenuForRows: true,
|
|
43
|
+
totalLabelsEditable,
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
else if (config.isInEditMode) {
|
|
48
|
+
tableConfig = {
|
|
49
|
+
...tableConfig,
|
|
50
|
+
menu: {
|
|
51
|
+
...tableConfig.menu,
|
|
52
|
+
totalLabelsEditable,
|
|
39
53
|
},
|
|
40
54
|
};
|
|
41
55
|
}
|
|
@@ -251,11 +265,17 @@ export class PluggablePivotTableNext extends AbstractPluggableVisualization {
|
|
|
251
265
|
}
|
|
252
266
|
const { customVisualizationConfig = {}, theme, custom = {}, config = {} } = options;
|
|
253
267
|
const { drillableItems } = custom;
|
|
254
|
-
|
|
268
|
+
// A renamed total's alias is a widget-level override on dashboards (see handlePushData) -
|
|
269
|
+
// it must land on the buckets used to build the execution, since the alias is part of the
|
|
270
|
+
// total's own definition, not just how it's displayed.
|
|
271
|
+
const insightWithTotalsOverride = this.environment === DASHBOARDS_ENVIRONMENT
|
|
272
|
+
? applyTotalsOverride(insight, getTotalsOverrideFromProperties(insightProperties(insight)))
|
|
273
|
+
: insight;
|
|
274
|
+
const execution = this.getExecution(options, insightWithTotalsOverride, executionFactory);
|
|
255
275
|
// Extract bucket data to send down the pivot table
|
|
256
|
-
const measuresBucket = insightBucket(
|
|
257
|
-
const rowsBucket = insightBucket(
|
|
258
|
-
const columnsBucket = insightBucket(
|
|
276
|
+
const measuresBucket = insightBucket(insightWithTotalsOverride, BucketNames.MEASURES);
|
|
277
|
+
const rowsBucket = insightBucket(insightWithTotalsOverride, BucketNames.ATTRIBUTE);
|
|
278
|
+
const columnsBucket = insightBucket(insightWithTotalsOverride, BucketNames.COLUMNS);
|
|
259
279
|
const measures = measuresBucket ? bucketMeasures(measuresBucket) : [];
|
|
260
280
|
const rows = rowsBucket ? bucketAttributes(rowsBucket) : [];
|
|
261
281
|
const columns = columnsBucket ? bucketAttributes(columnsBucket) : [];
|
|
@@ -364,6 +384,29 @@ export class PluggablePivotTableNext extends AbstractPluggableVisualization {
|
|
|
364
384
|
},
|
|
365
385
|
});
|
|
366
386
|
}
|
|
387
|
+
// On a dashboard there is no shared-insight bucket to persist a renamed/reset total's alias
|
|
388
|
+
// into (unlike AD, which writes it straight onto the reference point's buckets via its own
|
|
389
|
+
// properties saga) - store it as a widget-level controls override instead, keyed by bucket,
|
|
390
|
+
// merged with whatever the OTHER bucket already had, since CHANGE_PROPERTIES replaces all
|
|
391
|
+
// widget properties wholesale.
|
|
392
|
+
persistTotalsOverride(totals, bucketType) {
|
|
393
|
+
const properties = this.visualizationProperties ?? {};
|
|
394
|
+
const previousTotalsOverride = getTotalsOverrideFromProperties(properties);
|
|
395
|
+
const currentBucket = insightBucket(this.currentInsight, bucketType);
|
|
396
|
+
const changedTotals = getChangedTotals(totals, currentBucket ? bucketTotals(currentBucket) : []);
|
|
397
|
+
this.pushData({
|
|
398
|
+
properties: {
|
|
399
|
+
...properties,
|
|
400
|
+
controls: {
|
|
401
|
+
...properties.controls,
|
|
402
|
+
totals: {
|
|
403
|
+
...previousTotalsOverride,
|
|
404
|
+
[bucketType]: changedTotals,
|
|
405
|
+
},
|
|
406
|
+
},
|
|
407
|
+
},
|
|
408
|
+
});
|
|
409
|
+
}
|
|
367
410
|
handlePushData(data) {
|
|
368
411
|
if (data?.properties?.sortItems) {
|
|
369
412
|
// Handle sort items with optional totals
|
|
@@ -377,17 +420,27 @@ export class PluggablePivotTableNext extends AbstractPluggableVisualization {
|
|
|
377
420
|
};
|
|
378
421
|
this.pushData({ properties });
|
|
379
422
|
}
|
|
423
|
+
else if (data?.properties?.totals && this.environment === DASHBOARDS_ENVIRONMENT) {
|
|
424
|
+
this.persistTotalsOverride(data.properties.totals, data.properties.bucketType);
|
|
425
|
+
}
|
|
380
426
|
else if (data?.properties?.controls) {
|
|
381
|
-
// Enrich with current column widths if not present so they do not
|
|
427
|
+
// Enrich with current column widths and totals override if not present so they do not
|
|
428
|
+
// get lost in other properties changing (CHANGE_PROPERTIES replaces all widget
|
|
429
|
+
// properties wholesale - e.g. toggling header text wrapping must not silently revert a
|
|
430
|
+
// previously persisted total rename).
|
|
382
431
|
const columnWidths = getColumnWidthsFromProperties(data.properties) ??
|
|
383
432
|
getColumnWidthsFromProperties(this.visualizationProperties);
|
|
384
433
|
const shouldAddColumnWidths = !data.properties.controls.columnWidths;
|
|
434
|
+
const totals = getTotalsOverrideFromProperties(data.properties) ??
|
|
435
|
+
getTotalsOverrideFromProperties(this.visualizationProperties);
|
|
436
|
+
const shouldAddTotals = !data.properties.controls.totals && !!totals;
|
|
385
437
|
const properties = {
|
|
386
438
|
...data.properties,
|
|
387
|
-
...(shouldAddColumnWidths && {
|
|
439
|
+
...((shouldAddColumnWidths || shouldAddTotals) && {
|
|
388
440
|
controls: {
|
|
389
441
|
...data.properties.controls,
|
|
390
|
-
columnWidths,
|
|
442
|
+
...(shouldAddColumnWidths && { columnWidths }),
|
|
443
|
+
...(shouldAddTotals && { totals }),
|
|
391
444
|
},
|
|
392
445
|
}),
|
|
393
446
|
};
|
|
@@ -1,4 +1,21 @@
|
|
|
1
|
-
import { type ITotal } from "@gooddata/sdk-model";
|
|
1
|
+
import { type IInsightDefinition, type ITotal } from "@gooddata/sdk-model";
|
|
2
2
|
import { type IBucketFilter } from "../../../interfaces/Visualization.js";
|
|
3
|
+
import { type PivotTableTotalsOverride } from "../../../utils/propertiesHelper.js";
|
|
3
4
|
export declare const removeInvalidTotals: (totals: ITotal[], filters: IBucketFilter[]) => ITotal[];
|
|
5
|
+
export declare function getChangedTotals(totals: ITotal[], currentTotals: ITotal[]): ITotal[];
|
|
6
|
+
/**
|
|
7
|
+
* Applies a dashboard widget's totals-alias override onto the matching buckets of an insight, by
|
|
8
|
+
* bucket localIdentifier. Renaming a total's alias changes what the execution reports for it (it is
|
|
9
|
+
* part of the bucket's total definition, not just display), so the override must land on the
|
|
10
|
+
* buckets used to build the execution, not merely be read at render time.
|
|
11
|
+
*
|
|
12
|
+
* The override is applied as an ALIAS PATCH onto the insight's CURRENT totals, never as a wholesale
|
|
13
|
+
* replacement: the override was captured at rename time and can go stale if the insight's own
|
|
14
|
+
* totals later change (a measure/total added or removed in AD). Replacing the bucket's totals with
|
|
15
|
+
* the frozen override array would silently drop the insight's own new totals, or reintroduce totals
|
|
16
|
+
* for measures/attributes that no longer exist. Matching by definition (not array position) means a
|
|
17
|
+
* current total no longer present in the override keeps its own alias untouched, and an override
|
|
18
|
+
* entry no longer matching any current total is simply dropped.
|
|
19
|
+
*/
|
|
20
|
+
export declare function applyTotalsOverride<T extends IInsightDefinition>(insight: T, totalsOverride: PivotTableTotalsOverride | undefined): T;
|
|
4
21
|
//# sourceMappingURL=totalsHelpers.d.ts.map
|
package/esm/internal/components/pluggableVisualizations/pivotTableNext/totalsHelpers.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"totalsHelpers.d.ts","sourceRoot":"","sources":["../../../../../src/internal/components/pluggableVisualizations/pivotTableNext/totalsHelpers.ts"],"names":[],"mappings":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"totalsHelpers.d.ts","sourceRoot":"","sources":["../../../../../src/internal/components/pluggableVisualizations/pivotTableNext/totalsHelpers.ts"],"names":[],"mappings":"AAEA,OAAO,EACH,KAAK,kBAAkB,EACvB,KAAK,MAAM,EAMd,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,sCAAsC,CAAC;AAE1E,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,oCAAoC,CAAC;AAanF,eAAO,MAAM,mBAAmB,WAAY,MAAM,EAAE,WAAW,aAAa,EAAE,KAAG,MAAM,EAEtF,CAAC;AAUF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAKpF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,kBAAkB,EAC5D,OAAO,EAAE,CAAC,EACV,cAAc,EAAE,wBAAwB,GAAG,SAAS,GACrD,CAAC,CA6BH"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// (C) 2025 GoodData Corporation
|
|
2
|
-
import { totalIsNative } from "@gooddata/sdk-model";
|
|
1
|
+
// (C) 2025-2026 GoodData Corporation
|
|
2
|
+
import { bucketSetTotals, bucketTotals, insightBuckets, insightSetBuckets, totalIsNative, } from "@gooddata/sdk-model";
|
|
3
3
|
import { isActiveMeasureValueFilter, isRankingFilter } from "../../../utils/bucketHelper.js";
|
|
4
4
|
const isNativeTotalInvalid = (total, hasRankingFilter, hasMeasureValueFilter) => {
|
|
5
5
|
return totalIsNative(total) && (hasRankingFilter || hasMeasureValueFilter);
|
|
@@ -12,3 +12,52 @@ const removeInvalidNativeTotals = (totals, filters) => {
|
|
|
12
12
|
export const removeInvalidTotals = (totals, filters) => {
|
|
13
13
|
return removeInvalidNativeTotals(totals, filters);
|
|
14
14
|
};
|
|
15
|
+
function isSameTotalDefinition(first, second) {
|
|
16
|
+
return (first.type === second.type &&
|
|
17
|
+
first.measureIdentifier === second.measureIdentifier &&
|
|
18
|
+
first.attributeIdentifier === second.attributeIdentifier);
|
|
19
|
+
}
|
|
20
|
+
export function getChangedTotals(totals, currentTotals) {
|
|
21
|
+
return totals.filter((total) => {
|
|
22
|
+
const currentTotal = currentTotals.find((current) => isSameTotalDefinition(current, total));
|
|
23
|
+
return currentTotal?.alias !== total.alias;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Applies a dashboard widget's totals-alias override onto the matching buckets of an insight, by
|
|
28
|
+
* bucket localIdentifier. Renaming a total's alias changes what the execution reports for it (it is
|
|
29
|
+
* part of the bucket's total definition, not just display), so the override must land on the
|
|
30
|
+
* buckets used to build the execution, not merely be read at render time.
|
|
31
|
+
*
|
|
32
|
+
* The override is applied as an ALIAS PATCH onto the insight's CURRENT totals, never as a wholesale
|
|
33
|
+
* replacement: the override was captured at rename time and can go stale if the insight's own
|
|
34
|
+
* totals later change (a measure/total added or removed in AD). Replacing the bucket's totals with
|
|
35
|
+
* the frozen override array would silently drop the insight's own new totals, or reintroduce totals
|
|
36
|
+
* for measures/attributes that no longer exist. Matching by definition (not array position) means a
|
|
37
|
+
* current total no longer present in the override keeps its own alias untouched, and an override
|
|
38
|
+
* entry no longer matching any current total is simply dropped.
|
|
39
|
+
*/
|
|
40
|
+
export function applyTotalsOverride(insight, totalsOverride) {
|
|
41
|
+
if (!totalsOverride) {
|
|
42
|
+
return insight;
|
|
43
|
+
}
|
|
44
|
+
const buckets = insightBuckets(insight).map((bucket) => {
|
|
45
|
+
const overrideTotals = bucket.localIdentifier ? totalsOverride[bucket.localIdentifier] : undefined;
|
|
46
|
+
if (!overrideTotals) {
|
|
47
|
+
return bucket;
|
|
48
|
+
}
|
|
49
|
+
const mergedTotals = bucketTotals(bucket).map((total) => {
|
|
50
|
+
const overrideMatch = overrideTotals.find((override) => isSameTotalDefinition(override, total));
|
|
51
|
+
if (!overrideMatch) {
|
|
52
|
+
return total;
|
|
53
|
+
}
|
|
54
|
+
if (overrideMatch.alias === undefined) {
|
|
55
|
+
const { alias: _alias, ...totalWithoutAlias } = total;
|
|
56
|
+
return totalWithoutAlias;
|
|
57
|
+
}
|
|
58
|
+
return { ...total, alias: overrideMatch.alias };
|
|
59
|
+
});
|
|
60
|
+
return bucketSetTotals(bucket, mergedTotals);
|
|
61
|
+
});
|
|
62
|
+
return insightSetBuckets(insight, buckets);
|
|
63
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"supportedProperties.d.ts","sourceRoot":"","sources":["../../../src/internal/constants/supportedProperties.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AAEpF,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAqEjC,eAAO,MAAM,4BAA4B,UAA8C,CAAC;AAIxF,eAAO,MAAM,+BAA+B,UAM3C,CAAC;AAEF,eAAO,MAAM,+BAA+B,UAO3C,CAAC;AAEF,eAAO,MAAM,iCAAiC;IAC1C,CAAC,IAAI,CAAC,OAAO,CAAC,WAAoF;IAClG,CAAC,IAAI,CAAC,SAAS,CAAC,WAMf;IACD,CAAC,IAAI,CAAC,IAAI,CAAC,WAKV;CACJ,CAAC;AAEF,eAAO,MAAM,+BAA+B;IACxC,CAAC,IAAI,CAAC,OAAO,CAAC,WAQb;IACD,CAAC,IAAI,CAAC,SAAS,CAAC,WAYf;IACD,CAAC,IAAI,CAAC,IAAI,CAAC,WASV;CACJ,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,gCAAgC,UAa5C,CAAC;AAEF,eAAO,MAAM,8BAA8B;IACvC,CAAC,IAAI,CAAC,OAAO,CAAC,WAMb;IACD,CAAC,IAAI,CAAC,SAAS,CAAC,WAMf;IACD,CAAC,IAAI,CAAC,IAAI,CAAC,WAOV;CACJ,CAAC;AAEF,eAAO,MAAM,gCAAgC;IACzC,CAAC,IAAI,CAAC,OAAO,CAAC,WAUb;IACD,CAAC,IAAI,CAAC,SAAS,CAAC,WAYf;IACD,CAAC,IAAI,CAAC,IAAI,CAAC,WAWV;CACJ,CAAC;AAEF,eAAO,MAAM,iCAAiC,UAyB7C,CAAC;AAEF,eAAO,MAAM,gCAAgC,UAkC5C,CAAC;AAEF,eAAO,MAAM,0BAA0B,UAmBtC,CAAC;AAEF,eAAO,MAAM,6BAA6B,UAezC,CAAC;AAEF,eAAO,MAAM,gCAAgC,UAe5C,CAAC;AAEF,eAAO,MAAM,4BAA4B,UAcxC,CAAC;AAEF,eAAO,MAAM,4BAA4B,UAwBxC,CAAC;AAEF,eAAO,MAAM,iCAAiC,UA4B7C,CAAC;AAEF,eAAO,MAAM,+BAA+B,UAwB3C,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,oCAAoC,UAGhD,CAAC;AAEF,eAAO,MAAM,4BAA4B,UAiBxC,CAAC;AAEF,eAAO,MAAM,gCAAgC,UAW5C,CAAC;AAEF,eAAO,MAAM,qCAAqC,
|
|
1
|
+
{"version":3,"file":"supportedProperties.d.ts","sourceRoot":"","sources":["../../../src/internal/constants/supportedProperties.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AAEpF,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAqEjC,eAAO,MAAM,4BAA4B,UAA8C,CAAC;AAIxF,eAAO,MAAM,+BAA+B,UAM3C,CAAC;AAEF,eAAO,MAAM,+BAA+B,UAO3C,CAAC;AAEF,eAAO,MAAM,iCAAiC;IAC1C,CAAC,IAAI,CAAC,OAAO,CAAC,WAAoF;IAClG,CAAC,IAAI,CAAC,SAAS,CAAC,WAMf;IACD,CAAC,IAAI,CAAC,IAAI,CAAC,WAKV;CACJ,CAAC;AAEF,eAAO,MAAM,+BAA+B;IACxC,CAAC,IAAI,CAAC,OAAO,CAAC,WAQb;IACD,CAAC,IAAI,CAAC,SAAS,CAAC,WAYf;IACD,CAAC,IAAI,CAAC,IAAI,CAAC,WASV;CACJ,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,gCAAgC,UAa5C,CAAC;AAEF,eAAO,MAAM,8BAA8B;IACvC,CAAC,IAAI,CAAC,OAAO,CAAC,WAMb;IACD,CAAC,IAAI,CAAC,SAAS,CAAC,WAMf;IACD,CAAC,IAAI,CAAC,IAAI,CAAC,WAOV;CACJ,CAAC;AAEF,eAAO,MAAM,gCAAgC;IACzC,CAAC,IAAI,CAAC,OAAO,CAAC,WAUb;IACD,CAAC,IAAI,CAAC,SAAS,CAAC,WAYf;IACD,CAAC,IAAI,CAAC,IAAI,CAAC,WAWV;CACJ,CAAC;AAEF,eAAO,MAAM,iCAAiC,UAyB7C,CAAC;AAEF,eAAO,MAAM,gCAAgC,UAkC5C,CAAC;AAEF,eAAO,MAAM,0BAA0B,UAmBtC,CAAC;AAEF,eAAO,MAAM,6BAA6B,UAezC,CAAC;AAEF,eAAO,MAAM,gCAAgC,UAe5C,CAAC;AAEF,eAAO,MAAM,4BAA4B,UAcxC,CAAC;AAEF,eAAO,MAAM,4BAA4B,UAwBxC,CAAC;AAEF,eAAO,MAAM,iCAAiC,UA4B7C,CAAC;AAEF,eAAO,MAAM,+BAA+B,UAwB3C,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,oCAAoC,UAGhD,CAAC;AAEF,eAAO,MAAM,4BAA4B,UAiBxC,CAAC;AAEF,eAAO,MAAM,gCAAgC,UAW5C,CAAC;AAEF,eAAO,MAAM,qCAAqC,UAiBjD,CAAC;AAEF,eAAO,MAAM,iCAAiC,UAY7C,CAAC;AAWF,eAAO,MAAM,oCAAoC,UAQhD,CAAC;AAEF,eAAO,MAAM,kCAAkC,UAW9C,CAAC;AAEF,eAAO,MAAM,6BAA6B,UAMzC,CAAC;AAEF,eAAO,MAAM,mCAAmC,EAAE,yBAIjD,CAAC;AAEF,eAAO,MAAM,6CAA6C,EAAE,yBAS3D,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type IConditionalFormatting, type IInsightDefinition, type ISettings } from "@gooddata/sdk-model";
|
|
1
|
+
import { type IConditionalFormatting, type IInsightDefinition, type ISettings, type ITotal } from "@gooddata/sdk-model";
|
|
2
2
|
import { type ColumnHeadersPosition, type ColumnWidthItem, type GrandTotalsPosition, type MeasureGroupDimension } from "@gooddata/sdk-ui-pivot";
|
|
3
3
|
import { type IPagination, type ITextWrapping, type PivotTableNextConditionalFormattingConfig } from "@gooddata/sdk-ui-pivot/next";
|
|
4
4
|
import { type AxisType } from "../interfaces/AxisType.js";
|
|
@@ -20,6 +20,14 @@ export declare function getDataPointsConfiguration(controlProperties: IVisualiza
|
|
|
20
20
|
export declare function getColumnWidthsFromProperties(visualizationProperties: IVisualizationProperties): ColumnWidthItem[] | undefined;
|
|
21
21
|
export declare function getTextWrappingFromProperties(visualizationProperties: IVisualizationProperties): ITextWrapping | undefined;
|
|
22
22
|
export declare function getGrandTotalsPositionFromProperties(visualizationProperties: IVisualizationProperties): GrandTotalsPosition | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Widget-level override of totals' aliases, keyed by the bucket's localIdentifier ("attribute" or
|
|
25
|
+
* "columns"). Dashboards have no shared-insight bucket to persist a rename into (unlike AD, which
|
|
26
|
+
* writes it straight onto the reference point's buckets), so this lives in the widget's own
|
|
27
|
+
* properties instead - see PluggablePivotTableNext for where it's written and applied.
|
|
28
|
+
*/
|
|
29
|
+
export type PivotTableTotalsOverride = Record<string, ITotal[]>;
|
|
30
|
+
export declare function getTotalsOverrideFromProperties(visualizationProperties: IVisualizationProperties): PivotTableTotalsOverride | undefined;
|
|
23
31
|
export declare function getPaginationFromProperties(visualizationProperties: IVisualizationProperties): IPagination | undefined;
|
|
24
32
|
export declare function getConditionalFormattingFromProperties(visualizationProperties: IVisualizationProperties): PivotTableNextConditionalFormattingConfig["conditionalFormatting"] | undefined;
|
|
25
33
|
/**
|
|
@@ -71,5 +79,6 @@ export declare function getPivotTableProperties(settings: ISettings, properties:
|
|
|
71
79
|
columnHeadersPosition: ColumnHeadersPosition;
|
|
72
80
|
textWrapping?: ITextWrapping | undefined;
|
|
73
81
|
grandTotalsPosition?: GrandTotalsPosition | undefined;
|
|
82
|
+
totals?: PivotTableTotalsOverride | undefined;
|
|
74
83
|
};
|
|
75
84
|
//# sourceMappingURL=propertiesHelper.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"propertiesHelper.d.ts","sourceRoot":"","sources":["../../../src/internal/utils/propertiesHelper.ts"],"names":[],"mappings":"AAKA,OAAO,EACH,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,KAAK,SAAS,
|
|
1
|
+
{"version":3,"file":"propertiesHelper.d.ts","sourceRoot":"","sources":["../../../src/internal/utils/propertiesHelper.ts"],"names":[],"mappings":"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"}
|
|
@@ -184,6 +184,9 @@ export function getTextWrappingFromProperties(visualizationProperties) {
|
|
|
184
184
|
export function getGrandTotalsPositionFromProperties(visualizationProperties) {
|
|
185
185
|
return visualizationProperties?.controls?.["grandTotalsPosition"];
|
|
186
186
|
}
|
|
187
|
+
export function getTotalsOverrideFromProperties(visualizationProperties) {
|
|
188
|
+
return visualizationProperties?.controls?.["totals"];
|
|
189
|
+
}
|
|
187
190
|
export function getPaginationFromProperties(visualizationProperties) {
|
|
188
191
|
return visualizationProperties?.controls?.["pagination"];
|
|
189
192
|
}
|
|
@@ -291,10 +294,14 @@ export function getPivotTableProperties(settings, properties) {
|
|
|
291
294
|
const grandTotalsPosition = enableNewPivotTable
|
|
292
295
|
? getGrandTotalsPositionFromProperties(properties)
|
|
293
296
|
: undefined;
|
|
297
|
+
// Carried along so other controls changes (e.g. column resize) do not silently wipe out a
|
|
298
|
+
// previously persisted total rename - see PivotTableTotalsOverride.
|
|
299
|
+
const totalsOverride = enableNewPivotTable ? getTotalsOverrideFromProperties(properties) : undefined;
|
|
294
300
|
return {
|
|
295
301
|
measureGroupDimension: getMeasureGroupDimensionFromProperties(properties),
|
|
296
302
|
columnHeadersPosition: getColumnHeadersPositionFromProperties(properties),
|
|
297
303
|
...(textWrapping ? { textWrapping } : {}),
|
|
298
304
|
...(grandTotalsPosition ? { grandTotalsPosition } : {}),
|
|
305
|
+
...(totalsOverride ? { totals: totalsOverride } : {}),
|
|
299
306
|
};
|
|
300
307
|
}
|
package/esm/sdk-ui-ext.d.ts
CHANGED
|
@@ -1877,11 +1877,12 @@ export declare const isPercentFormat: (format: string | undefined) => boolean;
|
|
|
1877
1877
|
|
|
1878
1878
|
/**
|
|
1879
1879
|
* Whether an access-list error means object-level permissions aren't available to
|
|
1880
|
-
* the caller
|
|
1881
|
-
* (or label)
|
|
1882
|
-
*
|
|
1883
|
-
*
|
|
1884
|
-
*
|
|
1880
|
+
* the caller. The permissions endpoint is manage-gated and denies a caller who can't
|
|
1881
|
+
* manage the object (or label) with **403**; it answers **404** when the object is not
|
|
1882
|
+
* visible to the caller at all. Both are definitive: the caller has nothing to act on.
|
|
1883
|
+
* We match those two statuses and nothing else — a transient failure (5xx / network)
|
|
1884
|
+
* may still resolve, so it must not be read as a permanent "no". If the backend ever
|
|
1885
|
+
* signals this differently, widen here deliberately, in coordination with the backend.
|
|
1885
1886
|
*
|
|
1886
1887
|
* @internal
|
|
1887
1888
|
*/
|
|
@@ -1980,6 +1981,13 @@ export declare interface IUsePagedDropdownConfig {
|
|
|
1980
1981
|
* Only used by the enhanced insight picker — other consumers should leave this off.
|
|
1981
1982
|
*/
|
|
1982
1983
|
includeAuthorInfo?: boolean;
|
|
1984
|
+
/**
|
|
1985
|
+
* Search term owned by the caller. It replaces the hook's own search state, so that every page
|
|
1986
|
+
* the hook fetches is already narrowed by the server instead of by the caller afterwards. It is
|
|
1987
|
+
* matched across title, identifier, description and tags, unlike the tab-based term, which the
|
|
1988
|
+
* backend matches against the title alone.
|
|
1989
|
+
*/
|
|
1990
|
+
searchTerm?: string;
|
|
1983
1991
|
}
|
|
1984
1992
|
|
|
1985
1993
|
/**
|
|
@@ -2315,7 +2323,7 @@ export declare function useCopyObjectPermissions(): (from: IObjectPermissionsObj
|
|
|
2315
2323
|
*
|
|
2316
2324
|
* @internal
|
|
2317
2325
|
*/
|
|
2318
|
-
export declare function useInsightPagedList({ backend, workspaceId, author, tabsIds, tags, excludeTags, sortBy, sortDirection, createdByFilter, includeAuthorInfo, }: IUsePagedDropdownConfig): IUsePagedDropdownResult;
|
|
2326
|
+
export declare function useInsightPagedList({ backend, workspaceId, author, tabsIds, tags, excludeTags, sortBy, sortDirection, createdByFilter, includeAuthorInfo, searchTerm, }: IUsePagedDropdownConfig): IUsePagedDropdownResult;
|
|
2319
2327
|
|
|
2320
2328
|
/**
|
|
2321
2329
|
* Managed state for the InsightPicker component.
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Whether an access-list error means object-level permissions aren't available to
|
|
3
|
-
* the caller
|
|
4
|
-
* (or label)
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
3
|
+
* the caller. The permissions endpoint is manage-gated and denies a caller who can't
|
|
4
|
+
* manage the object (or label) with **403**; it answers **404** when the object is not
|
|
5
|
+
* visible to the caller at all. Both are definitive: the caller has nothing to act on.
|
|
6
|
+
* We match those two statuses and nothing else — a transient failure (5xx / network)
|
|
7
|
+
* may still resolve, so it must not be read as a permanent "no". If the backend ever
|
|
8
|
+
* signals this differently, widen here deliberately, in coordination with the backend.
|
|
8
9
|
*
|
|
9
10
|
* @internal
|
|
10
11
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accessErrors.d.ts","sourceRoot":"","sources":["../../src/share/accessErrors.ts"],"names":[],"mappings":"AAIA
|
|
1
|
+
{"version":3,"file":"accessErrors.d.ts","sourceRoot":"","sources":["../../src/share/accessErrors.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAEjE"}
|
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
import { isUnexpectedResponseError } from "@gooddata/sdk-backend-spi";
|
|
3
3
|
/**
|
|
4
4
|
* Whether an access-list error means object-level permissions aren't available to
|
|
5
|
-
* the caller
|
|
6
|
-
* (or label)
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* the caller. The permissions endpoint is manage-gated and denies a caller who can't
|
|
6
|
+
* manage the object (or label) with **403**; it answers **404** when the object is not
|
|
7
|
+
* visible to the caller at all. Both are definitive: the caller has nothing to act on.
|
|
8
|
+
* We match those two statuses and nothing else — a transient failure (5xx / network)
|
|
9
|
+
* may still resolve, so it must not be read as a permanent "no". If the backend ever
|
|
10
|
+
* signals this differently, widen here deliberately, in coordination with the backend.
|
|
10
11
|
*
|
|
11
12
|
* @internal
|
|
12
13
|
*/
|
|
13
14
|
export function isPermissionsNotAvailable(error) {
|
|
14
|
-
return isUnexpectedResponseError(error) && error.httpStatus === 404;
|
|
15
|
+
return isUnexpectedResponseError(error) && (error.httpStatus === 403 || error.httpStatus === 404);
|
|
15
16
|
}
|
|
@@ -102,9 +102,9 @@ export function useLabelScope(target, labels, hasList, committedGranteeIds, labe
|
|
|
102
102
|
setSelectedLabelIdsByGrantee({});
|
|
103
103
|
setDirectLabelIdsByGrantee({});
|
|
104
104
|
}
|
|
105
|
-
// Probe each label's access list to learn which are permissionable (some 404) and
|
|
105
|
+
// Probe each label's access list to learn which are permissionable (some 403/404) and
|
|
106
106
|
// which each grantee holds. `hasList` gates it: a list with no named grantees keeps
|
|
107
|
-
// `granteeIdsKey` empty, but the permissionable set (404 filtering) must still resolve.
|
|
107
|
+
// `granteeIdsKey` empty, but the permissionable set (403/404 filtering) must still resolve.
|
|
108
108
|
const { result: labelLists } = useCancelablePromise({
|
|
109
109
|
promise: target && labels.length > 0 && hasList
|
|
110
110
|
? () => Promise.all(labels.map((label) => backend
|
|
@@ -112,7 +112,7 @@ export function useLabelScope(target, labels, hasList, committedGranteeIds, labe
|
|
|
112
112
|
.objectPermissions()
|
|
113
113
|
.getAccessList({ kind: "label", ref: label.ref })
|
|
114
114
|
.then((list) => ({ label, list }))
|
|
115
|
-
// Only a definitive 404 means the label can't take a
|
|
115
|
+
// Only a definitive 403/404 means the label can't take a
|
|
116
116
|
// grant; a transient failure must NOT drop a real label.
|
|
117
117
|
.catch((error) => ({
|
|
118
118
|
label,
|
|
@@ -158,7 +158,7 @@ export function useLabelScope(target, labels, hasList, committedGranteeIds, labe
|
|
|
158
158
|
}
|
|
159
159
|
const permissionable = new Set();
|
|
160
160
|
for (const result of labelLists) {
|
|
161
|
-
// Keep transiently-failed labels permissionable; skip definitive 404s.
|
|
161
|
+
// Keep transiently-failed labels permissionable; skip definitive 403/404s.
|
|
162
162
|
if ("transient" in result) {
|
|
163
163
|
if (result.transient) {
|
|
164
164
|
permissionable.add(result.label.id);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gooddata/sdk-ui-ext",
|
|
3
|
-
"version": "11.57.0
|
|
3
|
+
"version": "11.57.0",
|
|
4
4
|
"description": "GoodData.UI SDK - Extensions",
|
|
5
5
|
"license": "LicenseRef-LICENSE",
|
|
6
6
|
"author": "GoodData Corporation",
|
|
@@ -63,20 +63,20 @@
|
|
|
63
63
|
"ts-invariant": "0.10.3",
|
|
64
64
|
"tslib": "2.8.1",
|
|
65
65
|
"uuid": "11.1.1",
|
|
66
|
-
"@gooddata/sdk-backend-base": "11.57.0
|
|
67
|
-
"@gooddata/sdk-backend-spi": "11.57.0
|
|
68
|
-
"@gooddata/sdk-embedding": "11.57.0
|
|
69
|
-
"@gooddata/sdk-
|
|
70
|
-
"@gooddata/sdk-
|
|
71
|
-
"@gooddata/sdk-ui-
|
|
72
|
-
"@gooddata/sdk-ui-
|
|
73
|
-
"@gooddata/sdk-ui-geo": "11.57.0
|
|
74
|
-
"@gooddata/sdk-ui-kit": "11.57.0
|
|
75
|
-
"@gooddata/sdk-ui-
|
|
76
|
-
"@gooddata/sdk-ui-
|
|
77
|
-
"@gooddata/sdk-ui-theme-provider": "11.57.0
|
|
78
|
-
"@gooddata/
|
|
79
|
-
"@gooddata/
|
|
66
|
+
"@gooddata/sdk-backend-base": "11.57.0",
|
|
67
|
+
"@gooddata/sdk-backend-spi": "11.57.0",
|
|
68
|
+
"@gooddata/sdk-embedding": "11.57.0",
|
|
69
|
+
"@gooddata/sdk-ui": "11.57.0",
|
|
70
|
+
"@gooddata/sdk-model": "11.57.0",
|
|
71
|
+
"@gooddata/sdk-ui-charts": "11.57.0",
|
|
72
|
+
"@gooddata/sdk-ui-filters": "11.57.0",
|
|
73
|
+
"@gooddata/sdk-ui-geo": "11.57.0",
|
|
74
|
+
"@gooddata/sdk-ui-kit": "11.57.0",
|
|
75
|
+
"@gooddata/sdk-ui-semantic-search": "11.57.0",
|
|
76
|
+
"@gooddata/sdk-ui-pivot": "11.57.0",
|
|
77
|
+
"@gooddata/sdk-ui-theme-provider": "11.57.0",
|
|
78
|
+
"@gooddata/sdk-ui-vis-commons": "11.57.0",
|
|
79
|
+
"@gooddata/util": "11.57.0"
|
|
80
80
|
},
|
|
81
81
|
"devDependencies": {
|
|
82
82
|
"@microsoft/api-documenter": "7.30.10",
|
|
@@ -120,12 +120,12 @@
|
|
|
120
120
|
"typescript": "7.0.2",
|
|
121
121
|
"vitest": "4.1.8",
|
|
122
122
|
"vitest-dom": "0.1.1",
|
|
123
|
-
"@gooddata/eslint-config": "11.57.0
|
|
124
|
-
"@gooddata/
|
|
125
|
-
"@gooddata/
|
|
126
|
-
"@gooddata/
|
|
127
|
-
"@gooddata/
|
|
128
|
-
"@gooddata/
|
|
123
|
+
"@gooddata/eslint-config": "11.57.0",
|
|
124
|
+
"@gooddata/oxlint-config": "11.57.0",
|
|
125
|
+
"@gooddata/i18n-toolkit": "11.57.0",
|
|
126
|
+
"@gooddata/sdk-backend-mockingbird": "11.57.0",
|
|
127
|
+
"@gooddata/reference-workspace": "11.57.0",
|
|
128
|
+
"@gooddata/stylelint-config": "11.57.0"
|
|
129
129
|
},
|
|
130
130
|
"peerDependencies": {
|
|
131
131
|
"react": "^18.0.0 || ^19.0.0",
|