@gooddata/sdk-ui-filters 11.54.0-alpha.4 → 11.54.0-alpha.5

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.
@@ -0,0 +1,34 @@
1
+ import { type ElementsQueryOptionsElementsSpecification, type IElementsQueryAttributeFilter } from "@gooddata/sdk-backend-spi";
2
+ import { type IAttributeElement, type IAttributeFilter, type IMeasure, type IRelativeDateFilter } from "@gooddata/sdk-model";
3
+ export declare const particularAttributeElements: ElementsQueryOptionsElementsSpecification;
4
+ export declare const anotherParticularAttributeElements: ElementsQueryOptionsElementsSpecification;
5
+ export declare const limitingAttributeFilters: IElementsQueryAttributeFilter[];
6
+ export declare const limitingMeasures: IMeasure[];
7
+ export declare const limitingDateFilters: IRelativeDateFilter[];
8
+ export declare const staticElements: IAttributeElement[];
9
+ export declare const hiddenElements: string[];
10
+ export declare const newTestAttributeFilterHandler: (useCase: "hidden" | "negative" | "nonExisting" | "positive" | "static") => import("./types/attributeFilterHandler.js").IMultiSelectAttributeFilterHandler;
11
+ export declare const positiveAttributeFilterDefaultDF: import("@gooddata/sdk-model").IPositiveAttributeFilter;
12
+ export declare const emptyPositiveAttributeFilterDefaultDF: import("@gooddata/sdk-model").IPositiveAttributeFilter;
13
+ export declare const negativeAttributeFilterDefaultDF: import("@gooddata/sdk-model").INegativeAttributeFilter;
14
+ export declare const newTestAttributeFilterHandlerWithAttributeFilter: (attributeFilter: IAttributeFilter, advancedOptions?: {
15
+ staticElements?: IAttributeElement[] | undefined;
16
+ hiddenElements?: string[] | undefined;
17
+ } | undefined) => import("./types/attributeFilterHandler.js").IMultiSelectAttributeFilterHandler;
18
+ /**
19
+ * Handler whose elements loads can be made to fail on demand.
20
+ *
21
+ * The failure is injected into the backend the handler talks to, so that the tests of the error
22
+ * paths do not have to mock the internal loadElements module. A module mock cannot be relied on
23
+ * here, because the test files share the module registry (see `isolate: false` in vitest.config.ts)
24
+ * and the module is already evaluated unmocked by the time such a suite runs.
25
+ */
26
+ export declare const newTestAttributeFilterHandlerWithElementsLoadFailures: (attributeFilter: IAttributeFilter) => {
27
+ attributeFilterHandler: import("./types/attributeFilterHandler.js").IMultiSelectAttributeFilterHandler;
28
+ /**
29
+ * Makes the next elements load reject with the provided error. Call it repeatedly to queue up
30
+ * more failures, one per load - the equivalent of several mockRejectedValueOnce() calls.
31
+ */
32
+ failNextElementsLoad: (error: unknown) => void;
33
+ };
34
+ //# sourceMappingURL=fixtures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixtures.d.ts","sourceRoot":"","sources":["../../src/AttributeFilterHandler/fixtures.ts"],"names":[],"mappings":"AAIA,OAAO,EACH,KAAK,yCAAyC,EAG9C,KAAK,6BAA6B,EACrC,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACH,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,QAAQ,EACb,KAAK,mBAAmB,EAQ3B,MAAM,qBAAqB,CAAC;AAI7B,eAAO,MAAM,2BAA2B,EAAE,yCAEzC,CAAC;AAEF,eAAO,MAAM,kCAAkC,EAAE,yCAEhD,CAAC;AAEF,eAAO,MAAM,wBAAwB,EAAE,6BAA6B,EAOnE,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,QAAQ,EAAyB,CAAC;AAEjE,eAAO,MAAM,mBAAmB,EAAE,mBAAmB,EAEpD,CAAC;AAyDF,eAAO,MAAM,cAAc,qBAKpB,CAAC;AAIR,eAAO,MAAM,cAAc,UAAsB,CAAC;AAElD,eAAO,MAAM,6BAA6B,4JAuBzC,CAAC;AAEF,eAAO,MAAM,gCAAgC,wDAE3C,CAAC;AAEH,eAAO,MAAM,qCAAqC,wDAEhD,CAAC;AAEH,eAAO,MAAM,gCAAgC,wDAE3C,CAAC;AAEH,eAAO,MAAM,gDAAgD;;;gGAU5D,CAAC;AA2EF;;;;;;;GAOG;AACH,eAAO,MAAM,qDAAqD;;IAU1D;;;OAGG;;CAKV,CAAC"}
@@ -0,0 +1,184 @@
1
+ // (C) 2019-2026 GoodData Corporation
2
+ import { ReferenceMd, ReferenceRecordings } from "@gooddata/reference-workspace";
3
+ import { recordedBackend } from "@gooddata/sdk-backend-mockingbird";
4
+ import { attributeIdentifier, idRef, measureIdentifier, newNegativeAttributeFilter, newPositiveAttributeFilter, newRelativeDateFilter, } from "@gooddata/sdk-model";
5
+ import { newAttributeFilterHandler } from "./factory.js";
6
+ export const particularAttributeElements = {
7
+ values: ["TouchAll", "WonderKid"],
8
+ };
9
+ export const anotherParticularAttributeElements = {
10
+ values: ["PhoenixSoft"],
11
+ };
12
+ export const limitingAttributeFilters = [
13
+ {
14
+ attributeFilter: newPositiveAttributeFilter(ReferenceMd.Product.Name, {
15
+ uris: ["460488"],
16
+ }),
17
+ overAttribute: idRef("attr.opportunitysnapshot.id"),
18
+ },
19
+ ];
20
+ export const limitingMeasures = [ReferenceMd.Amount];
21
+ export const limitingDateFilters = [
22
+ newRelativeDateFilter(ReferenceMd.DateDatasets.Snapshot.SnapshotDate.identifier, "GDC.time.date", 0, -1),
23
+ ];
24
+ const productNameAttrId = attributeIdentifier(ReferenceMd.Product.Name);
25
+ const amountMeasureId = measureIdentifier(ReferenceMd.Amount);
26
+ const snapshotDateId = ReferenceMd.DateDatasets.Snapshot.SnapshotDate.identifier;
27
+ const productDefaultId = attributeIdentifier(ReferenceMd.Product.Default);
28
+ const backend = recordedBackend(ReferenceRecordings.Recordings, {
29
+ attributeElementsFiltering: {
30
+ attributeFilters: {
31
+ [productNameAttrId]: (_element, index) => {
32
+ return (index + 1) % 2 === 0;
33
+ },
34
+ [productDefaultId]: (_element, index) => {
35
+ return (index + 1) % 2 === 0;
36
+ },
37
+ },
38
+ measures: {
39
+ [amountMeasureId]: (_element, index) => {
40
+ return (index + 1) % 3 === 0;
41
+ },
42
+ },
43
+ dateFilters: {
44
+ [snapshotDateId]: (_element, index) => {
45
+ return (index + 1) % 4 === 0;
46
+ },
47
+ },
48
+ },
49
+ });
50
+ const workspace = "testWorkspace";
51
+ const positiveAttributeFilter = newPositiveAttributeFilter(ReferenceMd.Product.Name, {
52
+ uris: ["165678", "165847"],
53
+ });
54
+ const emptyPositiveAttributeFilter = newPositiveAttributeFilter(ReferenceMd.Product.Name, {
55
+ uris: [],
56
+ });
57
+ const negativeAttributeFilter = newNegativeAttributeFilter(ReferenceMd.Product.Name, {
58
+ uris: ["165678", "165847"],
59
+ });
60
+ const nonExistingAttributeFilter = newPositiveAttributeFilter("non-existing-displayForm", {
61
+ uris: [],
62
+ });
63
+ const createDummyElements = (createElement, size) => new Array(size).fill(null).reduce((result, _, index) => {
64
+ result.push(createElement(index));
65
+ return result;
66
+ }, []);
67
+ export const staticElements = createDummyElements((index) => {
68
+ return {
69
+ title: `Element ${index}`,
70
+ uri: `/element?id=${index}`,
71
+ };
72
+ }, 100);
73
+ // This actually does not filter elements in mockingbirg
74
+ // It just triggers usage of the attributeElementsFiltering
75
+ export const hiddenElements = ["/hidden-element"];
76
+ export const newTestAttributeFilterHandler = (useCase) => {
77
+ let filter = positiveAttributeFilter;
78
+ const options = {
79
+ selectionMode: "multi",
80
+ };
81
+ if (useCase === "positive") {
82
+ filter = positiveAttributeFilter;
83
+ }
84
+ else if (useCase === "negative") {
85
+ filter = negativeAttributeFilter;
86
+ }
87
+ else if (useCase === "nonExisting") {
88
+ filter = nonExistingAttributeFilter;
89
+ }
90
+ else if (useCase === "static") {
91
+ filter = emptyPositiveAttributeFilter;
92
+ options.staticElements = staticElements;
93
+ }
94
+ else if (useCase === "hidden") {
95
+ filter = positiveAttributeFilter;
96
+ options.hiddenElements = hiddenElements;
97
+ }
98
+ return newAttributeFilterHandler(backend, workspace, filter, options);
99
+ };
100
+ export const positiveAttributeFilterDefaultDF = newPositiveAttributeFilter(ReferenceMd.Product.Default, {
101
+ uris: ["165678", "165847"],
102
+ });
103
+ export const emptyPositiveAttributeFilterDefaultDF = newPositiveAttributeFilter(ReferenceMd.Product.Default, {
104
+ uris: [],
105
+ });
106
+ export const negativeAttributeFilterDefaultDF = newNegativeAttributeFilter(ReferenceMd.Product.Default, {
107
+ uris: ["165678", "165847"],
108
+ });
109
+ export const newTestAttributeFilterHandlerWithAttributeFilter = (attributeFilter, advancedOptions) => {
110
+ const options = {
111
+ selectionMode: "multi",
112
+ ...(advancedOptions ?? {}),
113
+ };
114
+ return newAttributeFilterHandler(backend, workspace, attributeFilter, options);
115
+ };
116
+ const isFunction = (value) => typeof value === "function";
117
+ const isElementsQuery = (value) => typeof value === "object" && value !== null && isFunction(value.query);
118
+ /**
119
+ * Facade of `target` with a single method replaced.
120
+ *
121
+ * A proxy is used instead of an object spread, because the backend services are class instances -
122
+ * their methods live on the prototype and a spread would drop them.
123
+ */
124
+ const withReplacedMethod = (target, method, replacement) => new Proxy(target, {
125
+ get(proxiedTarget, property) {
126
+ if (property === method) {
127
+ return replacement;
128
+ }
129
+ const value = Reflect.get(proxiedTarget, property, proxiedTarget);
130
+ return isFunction(value) ? value.bind(proxiedTarget) : value;
131
+ },
132
+ });
133
+ /**
134
+ * Elements query that rejects with the queued errors, one error per query() call, and delegates to
135
+ * the recorded implementation once the queue is empty.
136
+ */
137
+ const withQueuedElementsLoadFailures = (elementsQuery, errors) => new Proxy(elementsQuery, {
138
+ get(proxiedTarget, property) {
139
+ const value = Reflect.get(proxiedTarget, property, proxiedTarget);
140
+ if (!isFunction(value)) {
141
+ return value;
142
+ }
143
+ if (property === "query") {
144
+ return (...args) => errors.length > 0 ? Promise.reject(errors.shift()) : value.apply(proxiedTarget, args);
145
+ }
146
+ return (...args) => {
147
+ const result = value.apply(proxiedTarget, args);
148
+ // The builder methods return the query itself, so the result has to be wrapped again
149
+ // to keep the interception in place for the rest of the chain.
150
+ return isElementsQuery(result) ? withQueuedElementsLoadFailures(result, errors) : result;
151
+ };
152
+ },
153
+ });
154
+ const backendWithQueuedElementsLoadFailures = (errors) => withReplacedMethod(backend, "workspace", (workspaceId) => {
155
+ const analyticalWorkspace = backend.workspace(workspaceId);
156
+ return withReplacedMethod(analyticalWorkspace, "attributes", () => {
157
+ const attributes = analyticalWorkspace.attributes();
158
+ return withReplacedMethod(attributes, "elements", () => {
159
+ const elements = attributes.elements();
160
+ return withReplacedMethod(elements, "forDisplayForm", (ref) => withQueuedElementsLoadFailures(elements.forDisplayForm(ref), errors));
161
+ });
162
+ });
163
+ });
164
+ /**
165
+ * Handler whose elements loads can be made to fail on demand.
166
+ *
167
+ * The failure is injected into the backend the handler talks to, so that the tests of the error
168
+ * paths do not have to mock the internal loadElements module. A module mock cannot be relied on
169
+ * here, because the test files share the module registry (see `isolate: false` in vitest.config.ts)
170
+ * and the module is already evaluated unmocked by the time such a suite runs.
171
+ */
172
+ export const newTestAttributeFilterHandlerWithElementsLoadFailures = (attributeFilter) => {
173
+ const errors = [];
174
+ return {
175
+ attributeFilterHandler: newAttributeFilterHandler(backendWithQueuedElementsLoadFailures(errors), workspace, attributeFilter, { selectionMode: "multi" }),
176
+ /**
177
+ * Makes the next elements load reject with the provided error. Call it repeatedly to queue up
178
+ * more failures, one per load - the equivalent of several mockRejectedValueOnce() calls.
179
+ */
180
+ failNextElementsLoad: (error) => {
181
+ errors.push(error);
182
+ },
183
+ };
184
+ };
@@ -0,0 +1,2 @@
1
+ export declare const waitForAsync: () => Promise<any>;
2
+ //# sourceMappingURL=testUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testUtils.d.ts","sourceRoot":"","sources":["../../src/AttributeFilterHandler/testUtils.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,YAAY,oBAAmF,CAAC"}
@@ -0,0 +1,2 @@
1
+ // (C) 2019-2026 GoodData Corporation
2
+ export const waitForAsync = () => new Promise((resolve) => setTimeout(resolve, 0));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gooddata/sdk-ui-filters",
3
- "version": "11.54.0-alpha.4",
3
+ "version": "11.54.0-alpha.5",
4
4
  "description": "GoodData.UI SDK - Filter Components",
5
5
  "license": "MIT",
6
6
  "author": "GoodData Corporation",
@@ -46,11 +46,11 @@
46
46
  "ts-invariant": "0.10.3",
47
47
  "tslib": "2.8.1",
48
48
  "uuid": "11.1.1",
49
- "@gooddata/sdk-model": "11.54.0-alpha.4",
50
- "@gooddata/sdk-backend-spi": "11.54.0-alpha.4",
51
- "@gooddata/sdk-ui-kit": "11.54.0-alpha.4",
52
- "@gooddata/sdk-ui": "11.54.0-alpha.4",
53
- "@gooddata/util": "11.54.0-alpha.4"
49
+ "@gooddata/sdk-backend-spi": "11.54.0-alpha.5",
50
+ "@gooddata/sdk-model": "11.54.0-alpha.5",
51
+ "@gooddata/sdk-ui": "11.54.0-alpha.5",
52
+ "@gooddata/sdk-ui-kit": "11.54.0-alpha.5",
53
+ "@gooddata/util": "11.54.0-alpha.5"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@microsoft/api-documenter": "^7.17.0",
@@ -92,12 +92,12 @@
92
92
  "typescript": "5.9.3",
93
93
  "vitest": "4.1.8",
94
94
  "vitest-dom": "0.1.1",
95
- "@gooddata/eslint-config": "11.54.0-alpha.4",
96
- "@gooddata/oxlint-config": "11.54.0-alpha.4",
97
- "@gooddata/reference-workspace": "11.54.0-alpha.4",
98
- "@gooddata/sdk-backend-mockingbird": "11.54.0-alpha.4",
99
- "@gooddata/stylelint-config": "11.54.0-alpha.4",
100
- "@gooddata/sdk-ui-theme-provider": "11.54.0-alpha.4"
95
+ "@gooddata/eslint-config": "11.54.0-alpha.5",
96
+ "@gooddata/oxlint-config": "11.54.0-alpha.5",
97
+ "@gooddata/reference-workspace": "11.54.0-alpha.5",
98
+ "@gooddata/sdk-backend-mockingbird": "11.54.0-alpha.5",
99
+ "@gooddata/sdk-ui-theme-provider": "11.54.0-alpha.5",
100
+ "@gooddata/stylelint-config": "11.54.0-alpha.5"
101
101
  },
102
102
  "peerDependencies": {
103
103
  "react": "^18.0.0 || ^19.0.0",