@cubejs-client/react 1.7.13 → 1.7.15
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/dist/CubeContext.d.ts +35 -0
- package/dist/CubeContext.d.ts.map +1 -0
- package/dist/CubeProvider.d.ts +31 -0
- package/dist/CubeProvider.d.ts.map +1 -0
- package/dist/QueryBuilder.d.ts +135 -0
- package/dist/QueryBuilder.d.ts.map +1 -0
- package/dist/QueryRenderer.d.ts +34 -0
- package/dist/QueryRenderer.d.ts.map +1 -0
- package/dist/QueryRendererWithTotals.d.ts +4 -0
- package/dist/QueryRendererWithTotals.d.ts.map +1 -0
- package/dist/cubejs-client-react.cjs.js +248 -23
- package/dist/cubejs-client-react.cjs.js.map +1 -1
- package/dist/cubejs-client-react.esm.js +253 -31
- package/dist/cubejs-client-react.esm.js.map +1 -1
- package/dist/cubejs-client-react.umd.js +248 -23
- package/dist/cubejs-client-react.umd.js.map +1 -1
- package/dist/hooks/cube-fetch.d.ts +6 -0
- package/dist/hooks/cube-fetch.d.ts.map +1 -0
- package/dist/hooks/cube-meta.d.ts +4 -0
- package/dist/hooks/cube-meta.d.ts.map +1 -0
- package/dist/hooks/cube-query.d.ts +39 -0
- package/dist/hooks/cube-query.d.ts.map +1 -0
- package/dist/hooks/cube-sql.d.ts +12 -0
- package/dist/hooks/cube-sql.d.ts.map +1 -0
- package/dist/hooks/deep-compare-memoize.d.ts +2 -0
- package/dist/hooks/deep-compare-memoize.d.ts.map +1 -0
- package/dist/hooks/dry-run.d.ts +7 -0
- package/dist/hooks/dry-run.d.ts.map +1 -0
- package/dist/hooks/lazy-dry-run.d.ts +7 -0
- package/dist/hooks/lazy-dry-run.d.ts.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/types.d.ts +765 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.d.ts.map +1 -0
- package/package.json +14 -10
- package/src/CubeContext.ts +37 -0
- package/src/CubeProvider.tsx +42 -0
- package/src/{QueryBuilder.jsx → QueryBuilder.tsx} +238 -89
- package/src/{QueryRenderer.jsx → QueryRenderer.tsx} +39 -14
- package/src/QueryRendererWithTotals.tsx +24 -0
- package/src/hooks/cube-fetch.ts +98 -0
- package/src/hooks/cube-meta.ts +8 -0
- package/src/hooks/{cube-query.js → cube-query.ts} +90 -14
- package/src/hooks/cube-sql.ts +22 -0
- package/src/hooks/{deep-compare-memoize.js → deep-compare-memoize.ts} +2 -2
- package/src/hooks/dry-run.ts +17 -0
- package/src/hooks/lazy-dry-run.ts +24 -0
- package/src/index.ts +77 -0
- package/src/types.ts +837 -0
- package/src/{utils.js → utils.ts} +3 -3
- package/index.d.ts +0 -697
- package/src/CubeContext.js +0 -3
- package/src/CubeProvider.jsx +0 -14
- package/src/QueryRendererWithTotals.jsx +0 -20
- package/src/hooks/cube-fetch.js +0 -66
- package/src/hooks/cube-meta.js +0 -5
- package/src/hooks/cube-sql.js +0 -8
- package/src/hooks/dry-run.js +0 -8
- package/src/hooks/lazy-dry-run.js +0 -11
- package/src/index.js +0 -18
|
@@ -12,12 +12,60 @@ import {
|
|
|
12
12
|
ResultSet,
|
|
13
13
|
removeEmptyQueryFields
|
|
14
14
|
} from '@cubejs-client/core';
|
|
15
|
+
import type {
|
|
16
|
+
CubeApi,
|
|
17
|
+
CubeMember,
|
|
18
|
+
DryRunResponse,
|
|
19
|
+
Meta,
|
|
20
|
+
NotFoundMember,
|
|
21
|
+
PivotConfig,
|
|
22
|
+
PivotQuery,
|
|
23
|
+
Query,
|
|
24
|
+
QueryOrder,
|
|
25
|
+
RequestError,
|
|
26
|
+
TCubeDimension,
|
|
27
|
+
TCubeMeasure,
|
|
28
|
+
TCubeSegment,
|
|
29
|
+
TDefaultHeuristicsState,
|
|
30
|
+
TOrderMember,
|
|
31
|
+
} from '@cubejs-client/core';
|
|
15
32
|
|
|
16
|
-
import QueryRenderer from './QueryRenderer
|
|
33
|
+
import QueryRenderer from './QueryRenderer';
|
|
17
34
|
import CubeContext from './CubeContext';
|
|
18
35
|
import { removeEmpty } from './utils';
|
|
19
|
-
|
|
20
|
-
|
|
36
|
+
import type {
|
|
37
|
+
AvailableCube,
|
|
38
|
+
AvailableMembers,
|
|
39
|
+
ChartType,
|
|
40
|
+
CubeContextProps,
|
|
41
|
+
FilterUpdateFields,
|
|
42
|
+
FilterUpdateInput,
|
|
43
|
+
GranularityOption,
|
|
44
|
+
IndexedDimension,
|
|
45
|
+
IndexedMeasure,
|
|
46
|
+
IndexedSegment,
|
|
47
|
+
MemberUpdater,
|
|
48
|
+
MemberUpdaterFactory,
|
|
49
|
+
MetaErrorSource,
|
|
50
|
+
MutexObj,
|
|
51
|
+
PivotConfigUpdaterArgs,
|
|
52
|
+
QueryBuilderInternalState,
|
|
53
|
+
QueryBuilderProps,
|
|
54
|
+
QueryBuilderRenderProps,
|
|
55
|
+
QueryBuilderState,
|
|
56
|
+
QueryBuilderStateUpdate,
|
|
57
|
+
QueryMemberType,
|
|
58
|
+
QueryRendererRenderProps,
|
|
59
|
+
ResolvedFilter,
|
|
60
|
+
ResolveMemberArgs,
|
|
61
|
+
ResolvedTimeDimension,
|
|
62
|
+
TimeDimensionComparisonUpdateFields,
|
|
63
|
+
TimeDimensionRangedUpdateFields,
|
|
64
|
+
TimeDimensionUpdateInput,
|
|
65
|
+
TimeDimensionWithExtraFields,
|
|
66
|
+
} from './types';
|
|
67
|
+
|
|
68
|
+
const granularities: GranularityOption[] = [
|
|
21
69
|
{ name: undefined, title: 'w/o grouping' },
|
|
22
70
|
{ name: 'second', title: 'Second' },
|
|
23
71
|
{ name: 'minute', title: 'Minute' },
|
|
@@ -29,7 +77,71 @@ const granularities = [
|
|
|
29
77
|
{ name: 'year', title: 'Year' },
|
|
30
78
|
];
|
|
31
79
|
|
|
32
|
-
|
|
80
|
+
/**
|
|
81
|
+
* `<QueryBuilder />` is used to build interactive analytics query builders. It abstracts state management and API calls to Cube.js Backend. It uses render prop technique and doesn’t render anything itself, but calls the render function instead.
|
|
82
|
+
*
|
|
83
|
+
* **Example**
|
|
84
|
+
*
|
|
85
|
+
* [Open in CodeSandbox](https://codesandbox.io/s/z6r7qj8wm)
|
|
86
|
+
* ```js
|
|
87
|
+
* import React from 'react';
|
|
88
|
+
* import ReactDOM from 'react-dom';
|
|
89
|
+
* import { Layout, Divider, Empty, Select } from 'antd';
|
|
90
|
+
* import { QueryBuilder } from '@cubejs-client/react';
|
|
91
|
+
* import cube from '@cubejs-client/core';
|
|
92
|
+
* import 'antd/dist/antd.css';
|
|
93
|
+
*
|
|
94
|
+
* import ChartRenderer from './ChartRenderer';
|
|
95
|
+
*
|
|
96
|
+
* const cubeApi = cube('YOUR-CUBE-API-TOKEN', {
|
|
97
|
+
* apiUrl: 'http://localhost:4000/cubejs-api/v1',
|
|
98
|
+
* });
|
|
99
|
+
*
|
|
100
|
+
* const App = () => (
|
|
101
|
+
* <QueryBuilder
|
|
102
|
+
* query={{
|
|
103
|
+
* timeDimensions: [
|
|
104
|
+
* {
|
|
105
|
+
* dimension: 'LineItems.createdAt',
|
|
106
|
+
* granularity: 'month',
|
|
107
|
+
* },
|
|
108
|
+
* ],
|
|
109
|
+
* }}
|
|
110
|
+
* cubeApi={cubeApi}
|
|
111
|
+
* render={({ resultSet, measures, availableMeasures, updateMeasures }) => (
|
|
112
|
+
* <Layout.Content style={{ padding: '20px' }}>
|
|
113
|
+
* <Select
|
|
114
|
+
* mode="multiple"
|
|
115
|
+
* style={{ width: '100%' }}
|
|
116
|
+
* placeholder="Please select"
|
|
117
|
+
* onSelect={(measure) => updateMeasures.add(measure)}
|
|
118
|
+
* onDeselect={(measure) => updateMeasures.remove(measure)}
|
|
119
|
+
* >
|
|
120
|
+
* {availableMeasures.map((measure) => (
|
|
121
|
+
* <Select.Option key={measure.name} value={measure}>
|
|
122
|
+
* {measure.title}
|
|
123
|
+
* </Select.Option>
|
|
124
|
+
* ))}
|
|
125
|
+
* </Select>
|
|
126
|
+
* <Divider />
|
|
127
|
+
* {measures.length > 0 ? (
|
|
128
|
+
* <ChartRenderer resultSet={resultSet} />
|
|
129
|
+
* ) : (
|
|
130
|
+
* <Empty description="Select measure or dimension to get started" />
|
|
131
|
+
* )}
|
|
132
|
+
* </Layout.Content>
|
|
133
|
+
* )}
|
|
134
|
+
* />
|
|
135
|
+
* );
|
|
136
|
+
*
|
|
137
|
+
* const rootElement = document.getElementById("root");
|
|
138
|
+
* ReactDOM.render(<App />, rootElement);
|
|
139
|
+
* ```
|
|
140
|
+
* @stickyTypes QueryBuilderProps, QueryBuilderRenderProps, QueryBuilderState
|
|
141
|
+
* @noInheritDoc
|
|
142
|
+
* @order 2
|
|
143
|
+
*/
|
|
144
|
+
export default class QueryBuilder extends React.Component<QueryBuilderProps, QueryBuilderInternalState> {
|
|
33
145
|
static contextType = CubeContext;
|
|
34
146
|
|
|
35
147
|
static defaultProps = {
|
|
@@ -52,7 +164,7 @@ export default class QueryBuilder extends React.Component {
|
|
|
52
164
|
|
|
53
165
|
// This is an anti-pattern, only kept for backward compatibility
|
|
54
166
|
// https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#anti-pattern-unconditionally-copying-props-to-state
|
|
55
|
-
static getDerivedStateFromProps(props, state) {
|
|
167
|
+
static getDerivedStateFromProps(props: QueryBuilderProps, state: QueryBuilderInternalState) {
|
|
56
168
|
if (props.query || props.vizState) {
|
|
57
169
|
const nextState = {
|
|
58
170
|
...state,
|
|
@@ -74,13 +186,16 @@ export default class QueryBuilder extends React.Component {
|
|
|
74
186
|
return null;
|
|
75
187
|
}
|
|
76
188
|
|
|
77
|
-
static resolveMember(
|
|
189
|
+
static resolveMember(
|
|
190
|
+
type: QueryMemberType,
|
|
191
|
+
{ meta, query }: ResolveMemberArgs
|
|
192
|
+
): unknown[] {
|
|
78
193
|
if (!meta) {
|
|
79
194
|
return [];
|
|
80
195
|
}
|
|
81
196
|
|
|
82
197
|
if (Array.isArray(query)) {
|
|
83
|
-
return query.reduce(
|
|
198
|
+
return query.reduce<unknown[]>(
|
|
84
199
|
(memo, currentQuery) => memo.concat(
|
|
85
200
|
QueryBuilder.resolveMember(type, {
|
|
86
201
|
meta,
|
|
@@ -95,7 +210,7 @@ export default class QueryBuilder extends React.Component {
|
|
|
95
210
|
return (query.timeDimensions || []).map((m, index) => ({
|
|
96
211
|
...m,
|
|
97
212
|
dimension: {
|
|
98
|
-
...meta.resolveMember(m.dimension, 'dimensions'),
|
|
213
|
+
...(meta.resolveMember(m.dimension, 'dimensions') as TCubeDimension),
|
|
99
214
|
granularities,
|
|
100
215
|
},
|
|
101
216
|
index,
|
|
@@ -104,15 +219,15 @@ export default class QueryBuilder extends React.Component {
|
|
|
104
219
|
|
|
105
220
|
return (query[type] || []).map((m, index) => ({
|
|
106
221
|
index,
|
|
107
|
-
...meta.resolveMember(m, type),
|
|
222
|
+
...(meta.resolveMember(m, type) as CubeMember),
|
|
108
223
|
}));
|
|
109
224
|
}
|
|
110
225
|
|
|
111
|
-
constructor(props) {
|
|
226
|
+
constructor(props: QueryBuilderProps) {
|
|
112
227
|
super(props);
|
|
113
228
|
|
|
114
229
|
this.state = {
|
|
115
|
-
query: props.defaultQuery || props.query,
|
|
230
|
+
query: (props.defaultQuery || props.query) as Query,
|
|
116
231
|
chartType: props.defaultChartType,
|
|
117
232
|
validatedQuery: props.query, // deprecated, validatedQuery should not be set until after dry-run for safety
|
|
118
233
|
missingMembers: [],
|
|
@@ -132,7 +247,7 @@ export default class QueryBuilder extends React.Component {
|
|
|
132
247
|
await this.fetchMeta();
|
|
133
248
|
}
|
|
134
249
|
|
|
135
|
-
async componentDidUpdate(prevProps) {
|
|
250
|
+
async componentDidUpdate(prevProps: QueryBuilderProps) {
|
|
136
251
|
const { schemaVersion, onSchemaChange } = this.props;
|
|
137
252
|
const { meta } = this.state;
|
|
138
253
|
|
|
@@ -146,7 +261,7 @@ export default class QueryBuilder extends React.Component {
|
|
|
146
261
|
const newMeta = await this.cubeApi().meta();
|
|
147
262
|
if (!equals(newMeta, meta) && typeof onSchemaChange === 'function') {
|
|
148
263
|
onSchemaChange({
|
|
149
|
-
schemaVersion,
|
|
264
|
+
schemaVersion: schemaVersion as number,
|
|
150
265
|
refresh: async () => {
|
|
151
266
|
await this.fetchMeta();
|
|
152
267
|
},
|
|
@@ -154,28 +269,44 @@ export default class QueryBuilder extends React.Component {
|
|
|
154
269
|
}
|
|
155
270
|
} catch (error) {
|
|
156
271
|
// eslint-disable-next-line
|
|
157
|
-
this.setState({ metaError: error });
|
|
272
|
+
this.setState({ metaError: error as Error });
|
|
158
273
|
}
|
|
159
274
|
}
|
|
160
275
|
}
|
|
161
276
|
|
|
277
|
+
// These sit after the lifecycle methods because `react/sort-comp` sorts
|
|
278
|
+
// TypeScript field declarations into `everything-else`, which the configured
|
|
279
|
+
// order puts after `lifecycle`. Runtime is unaffected: field initializers run
|
|
280
|
+
// right after `super()`, so the constructor's assignments still win.
|
|
281
|
+
//
|
|
282
|
+
// `this.context` is not re-declared: React types it as `any`, and a field
|
|
283
|
+
// declaration would be emitted at runtime and shadow the context React
|
|
284
|
+
// assigns.
|
|
285
|
+
private mutexObj: MutexObj;
|
|
286
|
+
|
|
287
|
+
private orderMembersOrderKeys: string[];
|
|
288
|
+
|
|
289
|
+
private prevContext?: CubeContextProps;
|
|
290
|
+
|
|
162
291
|
fetchMeta = async () => {
|
|
163
292
|
if (!this.cubeApi()) {
|
|
164
293
|
return;
|
|
165
294
|
}
|
|
166
295
|
|
|
167
|
-
let meta;
|
|
168
|
-
let metaError = null;
|
|
169
|
-
let richMetaError = null;
|
|
170
|
-
let metaErrorStack = null;
|
|
296
|
+
let meta: Meta | undefined;
|
|
297
|
+
let metaError: MetaErrorSource | null = null;
|
|
298
|
+
let richMetaError: Error | null = null;
|
|
299
|
+
let metaErrorStack: string | null = null;
|
|
171
300
|
|
|
172
301
|
try {
|
|
173
302
|
this.setState({ isFetchingMeta: true });
|
|
174
303
|
meta = await this.cubeApi().meta();
|
|
175
304
|
} catch (error) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
305
|
+
const requestError = error as RequestError;
|
|
306
|
+
|
|
307
|
+
metaError = requestError.response?.plainError || requestError;
|
|
308
|
+
richMetaError = requestError;
|
|
309
|
+
metaErrorStack = requestError.response?.stack?.replace(requestError.message || '', '') || '';
|
|
179
310
|
}
|
|
180
311
|
|
|
181
312
|
this.setState(
|
|
@@ -194,26 +325,26 @@ export default class QueryBuilder extends React.Component {
|
|
|
194
325
|
);
|
|
195
326
|
};
|
|
196
327
|
|
|
197
|
-
cubeApi() {
|
|
328
|
+
cubeApi(): CubeApi {
|
|
198
329
|
const { cubeApi } = this.props;
|
|
199
330
|
// eslint-disable-next-line react/destructuring-assignment
|
|
200
331
|
return cubeApi || (this.context && this.context.cubeApi);
|
|
201
332
|
}
|
|
202
333
|
|
|
203
|
-
getMissingMembers(query, meta) {
|
|
334
|
+
getMissingMembers(query: Query, meta?: Meta) {
|
|
204
335
|
if (!meta) {
|
|
205
336
|
return [];
|
|
206
337
|
}
|
|
207
338
|
|
|
208
339
|
return getQueryMembers(query)
|
|
209
340
|
.map((member) => {
|
|
210
|
-
const resolvedMember = meta.resolveMember(member, ['measures', 'dimensions', 'segments']);
|
|
341
|
+
const resolvedMember = meta.resolveMember(member, ['measures', 'dimensions', 'segments']) as NotFoundMember;
|
|
211
342
|
if (resolvedMember.error) {
|
|
212
343
|
return member;
|
|
213
344
|
}
|
|
214
345
|
return false;
|
|
215
346
|
})
|
|
216
|
-
.filter(Boolean);
|
|
347
|
+
.filter(Boolean) as string[];
|
|
217
348
|
}
|
|
218
349
|
|
|
219
350
|
isQueryPresent() {
|
|
@@ -221,10 +352,10 @@ export default class QueryBuilder extends React.Component {
|
|
|
221
352
|
return QueryRenderer.isQueryPresent(query);
|
|
222
353
|
}
|
|
223
354
|
|
|
224
|
-
prepareRenderProps(queryRendererProps) {
|
|
225
|
-
const getName = (member) => member.name;
|
|
355
|
+
prepareRenderProps(queryRendererProps?: QueryRendererRenderProps): QueryBuilderRenderProps {
|
|
356
|
+
const getName = (member: unknown) => (member as CubeMember).name;
|
|
226
357
|
|
|
227
|
-
const toTimeDimension = (member) => {
|
|
358
|
+
const toTimeDimension = (member: TimeDimensionUpdateInput) => {
|
|
228
359
|
const rangeSelection = member.compareDateRange
|
|
229
360
|
? { compareDateRange: member.compareDateRange }
|
|
230
361
|
: { dateRange: member.dateRange };
|
|
@@ -236,33 +367,33 @@ export default class QueryBuilder extends React.Component {
|
|
|
236
367
|
});
|
|
237
368
|
};
|
|
238
369
|
|
|
239
|
-
const toFilter = (member) => ({
|
|
370
|
+
const toFilter = (member: FilterUpdateInput) => ({
|
|
240
371
|
member: member.member?.name || member.dimension?.name,
|
|
241
372
|
operator: member.operator,
|
|
242
373
|
...(['set', 'notSet'].includes(member.operator) ? {} : { values: member.values }),
|
|
243
374
|
});
|
|
244
375
|
|
|
245
|
-
const updateMethods = (memberType, toQuery = getName) => ({
|
|
376
|
+
const updateMethods: MemberUpdaterFactory = (memberType, toQuery = getName) => ({
|
|
246
377
|
add: (member) => {
|
|
247
378
|
const { query } = this.state;
|
|
248
379
|
this.updateQuery({
|
|
249
|
-
[memberType]: (query[memberType] || []).concat(toQuery(member)),
|
|
250
|
-
});
|
|
380
|
+
[memberType]: ((query[memberType] || []) as unknown[]).concat(toQuery(member)),
|
|
381
|
+
} as Query);
|
|
251
382
|
},
|
|
252
383
|
remove: (member) => {
|
|
253
384
|
const { query } = this.state;
|
|
254
385
|
|
|
255
386
|
return this.updateQuery({
|
|
256
|
-
[memberType]: (query[memberType] || []).filter((_, index) => index !== member.index),
|
|
257
|
-
});
|
|
387
|
+
[memberType]: ((query[memberType] || []) as unknown[]).filter((_, index) => index !== member.index),
|
|
388
|
+
} as Query);
|
|
258
389
|
},
|
|
259
390
|
update: (member, updateWith) => {
|
|
260
391
|
const { query } = this.state;
|
|
261
|
-
const members = (query[memberType] || []).concat([]);
|
|
392
|
+
const members = ((query[memberType] || []) as unknown[]).concat([]);
|
|
262
393
|
members.splice(member.index, 1, toQuery(updateWith));
|
|
263
394
|
return this.updateQuery({
|
|
264
395
|
[memberType]: members,
|
|
265
|
-
});
|
|
396
|
+
} as Query);
|
|
266
397
|
},
|
|
267
398
|
});
|
|
268
399
|
|
|
@@ -287,27 +418,30 @@ export default class QueryBuilder extends React.Component {
|
|
|
287
418
|
member: filter.member || filter.dimension,
|
|
288
419
|
})));
|
|
289
420
|
|
|
290
|
-
const filters = flatFilters.map((m, i) => ({
|
|
421
|
+
const filters: ResolvedFilter[] = flatFilters.map((m, i) => ({
|
|
291
422
|
...m,
|
|
292
|
-
dimension: meta
|
|
293
|
-
|
|
423
|
+
dimension: meta!.resolveMember(
|
|
424
|
+
m.member || m.dimension!,
|
|
425
|
+
['dimensions', 'measures']
|
|
426
|
+
) as TCubeDimension | TCubeMeasure,
|
|
427
|
+
operators: meta!.filterOperatorsForMember(m.member || m.dimension!, ['dimensions', 'measures']),
|
|
294
428
|
index: i,
|
|
295
429
|
}));
|
|
296
430
|
|
|
297
|
-
const measures = QueryBuilder.resolveMember('measures', this.state);
|
|
298
|
-
const dimensions = QueryBuilder.resolveMember('dimensions', this.state);
|
|
299
|
-
const timeDimensions = QueryBuilder.resolveMember('timeDimensions', this.state);
|
|
300
|
-
const segments = ((meta && query.segments) || []).map((m, i) => ({
|
|
431
|
+
const measures = QueryBuilder.resolveMember('measures', this.state) as IndexedMeasure[];
|
|
432
|
+
const dimensions = QueryBuilder.resolveMember('dimensions', this.state) as IndexedDimension[];
|
|
433
|
+
const timeDimensions = QueryBuilder.resolveMember('timeDimensions', this.state) as ResolvedTimeDimension[];
|
|
434
|
+
const segments: IndexedSegment[] = ((meta && query.segments) || []).map((m, i) => ({
|
|
301
435
|
index: i,
|
|
302
|
-
...meta
|
|
436
|
+
...(meta!.resolveMember(m, 'segments') as TCubeSegment),
|
|
303
437
|
}));
|
|
304
438
|
|
|
305
|
-
let availableMeasures = [];
|
|
306
|
-
let availableDimensions = [];
|
|
307
|
-
let availableSegments = [];
|
|
308
|
-
let availableFilterMembers = [];
|
|
439
|
+
let availableMeasures: TCubeMeasure[] = [];
|
|
440
|
+
let availableDimensions: TCubeDimension[] = [];
|
|
441
|
+
let availableSegments: TCubeSegment[] = [];
|
|
442
|
+
let availableFilterMembers: Array<AvailableCube<TCubeMeasure> | AvailableCube<TCubeDimension>> = [];
|
|
309
443
|
|
|
310
|
-
const availableMembers = meta?.membersGroupedByCube() || {
|
|
444
|
+
const availableMembers: AvailableMembers = meta?.membersGroupedByCube() || {
|
|
311
445
|
measures: [],
|
|
312
446
|
dimensions: [],
|
|
313
447
|
segments: [],
|
|
@@ -315,15 +449,17 @@ export default class QueryBuilder extends React.Component {
|
|
|
315
449
|
};
|
|
316
450
|
|
|
317
451
|
if (meta) {
|
|
318
|
-
availableMeasures = meta.membersForQuery(query, 'measures');
|
|
319
|
-
availableDimensions = meta.membersForQuery(query, 'dimensions');
|
|
320
|
-
availableSegments = meta.membersForQuery(query, 'segments');
|
|
452
|
+
availableMeasures = meta.membersForQuery(query, 'measures') as TCubeMeasure[];
|
|
453
|
+
availableDimensions = meta.membersForQuery(query, 'dimensions') as TCubeDimension[];
|
|
454
|
+
availableSegments = meta.membersForQuery(query, 'segments') as TCubeSegment[];
|
|
321
455
|
|
|
322
456
|
const indexedMeasures = indexBy(prop('cubeName'), availableMembers.measures);
|
|
323
457
|
const indexedDimensions = indexBy(prop('cubeName'), availableMembers.dimensions);
|
|
324
458
|
const cubeNames = uniq([...Object.keys(indexedMeasures), ...Object.keys(indexedDimensions)]).sort();
|
|
325
459
|
|
|
326
|
-
|
|
460
|
+
// Measures and dimensions of a cube are merged into one member list, which
|
|
461
|
+
// the declared type describes as either one or the other
|
|
462
|
+
availableFilterMembers = cubeNames.map((name): AvailableCube<TCubeMeasure | TCubeDimension> => {
|
|
327
463
|
const cube = indexedMeasures[name] || indexedDimensions[name];
|
|
328
464
|
|
|
329
465
|
return {
|
|
@@ -333,7 +469,7 @@ export default class QueryBuilder extends React.Component {
|
|
|
333
469
|
...indexedDimensions[name]?.members
|
|
334
470
|
].sort((a, b) => (a.shortTitle > b.shortTitle ? 1 : -1)),
|
|
335
471
|
};
|
|
336
|
-
})
|
|
472
|
+
}) as Array<AvailableCube<TCubeMeasure> | AvailableCube<TCubeDimension>>;
|
|
337
473
|
}
|
|
338
474
|
|
|
339
475
|
const activeOrder = Array.isArray(query.order) ? Object.fromEntries(query.order) : query.order;
|
|
@@ -357,7 +493,7 @@ export default class QueryBuilder extends React.Component {
|
|
|
357
493
|
// This is needed so that when an order member becomes active, it doesn't jump to the top of the list
|
|
358
494
|
orderMembers = (this.orderMembersOrderKeys || [])
|
|
359
495
|
.map((id) => orderMembers.find((member) => member.id === id))
|
|
360
|
-
.filter(Boolean);
|
|
496
|
+
.filter(Boolean) as TOrderMember[];
|
|
361
497
|
}
|
|
362
498
|
|
|
363
499
|
return {
|
|
@@ -367,12 +503,14 @@ export default class QueryBuilder extends React.Component {
|
|
|
367
503
|
metaErrorStack,
|
|
368
504
|
query,
|
|
369
505
|
error: queryError, // Match same name as QueryRenderer prop
|
|
370
|
-
validatedQuery,
|
|
506
|
+
validatedQuery: validatedQuery as Query,
|
|
371
507
|
isQueryPresent: this.isQueryPresent(),
|
|
372
508
|
chartType,
|
|
373
509
|
measures,
|
|
374
510
|
dimensions,
|
|
375
|
-
|
|
511
|
+
// The declared granularity options require a `name`, which the
|
|
512
|
+
// "w/o grouping" option leaves out
|
|
513
|
+
timeDimensions: timeDimensions as (TimeDimensionWithExtraFields & { index: number })[],
|
|
376
514
|
segments,
|
|
377
515
|
filters,
|
|
378
516
|
orderMembers,
|
|
@@ -383,21 +521,25 @@ export default class QueryBuilder extends React.Component {
|
|
|
383
521
|
availableMembers,
|
|
384
522
|
availableFilterMembers,
|
|
385
523
|
updateQuery: (queryUpdate) => this.updateQuery(queryUpdate),
|
|
386
|
-
updateMeasures: updateMethods('measures'),
|
|
387
|
-
updateDimensions: updateMethods('dimensions'),
|
|
388
|
-
updateSegments: updateMethods('segments'),
|
|
389
|
-
updateTimeDimensions: updateMethods('timeDimensions', toTimeDimension)
|
|
390
|
-
|
|
391
|
-
|
|
524
|
+
updateMeasures: updateMethods<TCubeMeasure>('measures'),
|
|
525
|
+
updateDimensions: updateMethods<TCubeDimension>('dimensions'),
|
|
526
|
+
updateSegments: updateMethods<TCubeSegment>('segments'),
|
|
527
|
+
updateTimeDimensions: updateMethods<TimeDimensionUpdateInput>('timeDimensions', toTimeDimension) as
|
|
528
|
+
MemberUpdater<TimeDimensionRangedUpdateFields | TimeDimensionComparisonUpdateFields>,
|
|
529
|
+
updateFilters: updateMethods<FilterUpdateInput>('filters', toFilter) as MemberUpdater<FilterUpdateFields>,
|
|
530
|
+
updateChartType: (newChartType: ChartType) => this.updateVizState({ chartType: newChartType }),
|
|
392
531
|
updateOrder: {
|
|
393
|
-
set: (memberId, newOrder = 'asc') => {
|
|
532
|
+
set: (memberId: string, newOrder: QueryOrder | 'none' = 'asc') => {
|
|
394
533
|
this.updateQuery({
|
|
395
534
|
order: orderMembers
|
|
396
535
|
.map((orderMember) => ({
|
|
397
536
|
...orderMember,
|
|
398
537
|
order: orderMember.id === memberId ? newOrder : orderMember.order,
|
|
399
538
|
}))
|
|
400
|
-
.reduce
|
|
539
|
+
.reduce<[string, QueryOrder][]>(
|
|
540
|
+
(acc, { id, order }) => (order !== 'none' ? [...acc, [id, order]] : acc),
|
|
541
|
+
[]
|
|
542
|
+
),
|
|
401
543
|
});
|
|
402
544
|
},
|
|
403
545
|
update: (order) => {
|
|
@@ -405,7 +547,7 @@ export default class QueryBuilder extends React.Component {
|
|
|
405
547
|
order,
|
|
406
548
|
});
|
|
407
549
|
},
|
|
408
|
-
reorder: (sourceIndex, destinationIndex) => {
|
|
550
|
+
reorder: (sourceIndex: number, destinationIndex: number) => {
|
|
409
551
|
if (sourceIndex == null || destinationIndex == null) {
|
|
410
552
|
return;
|
|
411
553
|
}
|
|
@@ -414,15 +556,20 @@ export default class QueryBuilder extends React.Component {
|
|
|
414
556
|
this.orderMembersOrderKeys = nextArray.map(({ id }) => id);
|
|
415
557
|
|
|
416
558
|
this.updateQuery({
|
|
417
|
-
order: nextArray.reduce
|
|
559
|
+
order: nextArray.reduce<[string, QueryOrder][]>(
|
|
560
|
+
(acc, { id, order }) => (order !== 'none' ? [...acc, [id, order]] : acc),
|
|
561
|
+
[]
|
|
562
|
+
),
|
|
418
563
|
});
|
|
419
564
|
},
|
|
420
565
|
},
|
|
421
566
|
pivotConfig,
|
|
422
567
|
updatePivotConfig: {
|
|
423
|
-
moveItem: ({ sourceIndex, destinationIndex, sourceAxis, destinationAxis }) => {
|
|
568
|
+
moveItem: ({ sourceIndex, destinationIndex, sourceAxis, destinationAxis }: PivotConfigUpdaterArgs) => {
|
|
424
569
|
this.updateVizState({
|
|
425
|
-
pivotConfig: movePivotItem(
|
|
570
|
+
pivotConfig: movePivotItem(
|
|
571
|
+
pivotConfig as PivotConfig, sourceIndex, destinationIndex, sourceAxis, destinationAxis
|
|
572
|
+
),
|
|
426
573
|
});
|
|
427
574
|
},
|
|
428
575
|
update: (config) => {
|
|
@@ -440,12 +587,12 @@ export default class QueryBuilder extends React.Component {
|
|
|
440
587
|
missingMembers,
|
|
441
588
|
refresh: this.fetchMeta,
|
|
442
589
|
isFetchingMeta,
|
|
443
|
-
dryRunResponse,
|
|
590
|
+
dryRunResponse: dryRunResponse as DryRunResponse | undefined,
|
|
444
591
|
...queryRendererProps,
|
|
445
592
|
};
|
|
446
593
|
}
|
|
447
594
|
|
|
448
|
-
updateQuery(queryUpdate) {
|
|
595
|
+
updateQuery(queryUpdate: Query) {
|
|
449
596
|
const { query } = this.state;
|
|
450
597
|
|
|
451
598
|
this.updateVizState({
|
|
@@ -456,7 +603,7 @@ export default class QueryBuilder extends React.Component {
|
|
|
456
603
|
});
|
|
457
604
|
}
|
|
458
605
|
|
|
459
|
-
async updateVizState(state) {
|
|
606
|
+
async updateVizState(state: QueryBuilderStateUpdate) {
|
|
460
607
|
const { setQuery, setVizState } = this.props;
|
|
461
608
|
const { query: stateQuery, pivotConfig: statePivotConfig, chartType, meta } = this.state;
|
|
462
609
|
|
|
@@ -465,11 +612,11 @@ export default class QueryBuilder extends React.Component {
|
|
|
465
612
|
finalState.query = { ...stateQuery };
|
|
466
613
|
}
|
|
467
614
|
|
|
468
|
-
let vizStateSent = null;
|
|
469
|
-
const handleVizStateChange = (currentState) => {
|
|
615
|
+
let vizStateSent: QueryBuilderState | null = null;
|
|
616
|
+
const handleVizStateChange = (currentState: QueryBuilderStateUpdate) => {
|
|
470
617
|
const { onVizStateChanged } = this.props;
|
|
471
618
|
if (onVizStateChanged) {
|
|
472
|
-
const newVizState = pick(['chartType', 'pivotConfig', 'query'], currentState);
|
|
619
|
+
const newVizState = pick(['chartType', 'pivotConfig', 'query'], currentState) as QueryBuilderState;
|
|
473
620
|
// Don't run callbacks more than once unless the viz state has changed since last time
|
|
474
621
|
if (!vizStateSent || !equals(vizStateSent, newVizState)) {
|
|
475
622
|
onVizStateChanged(newVizState);
|
|
@@ -480,9 +627,9 @@ export default class QueryBuilder extends React.Component {
|
|
|
480
627
|
};
|
|
481
628
|
|
|
482
629
|
// deprecated, setters replaced by onVizStateChanged
|
|
483
|
-
const runSetters = (currentState) => {
|
|
630
|
+
const runSetters = (currentState: QueryBuilderStateUpdate) => {
|
|
484
631
|
if (setVizState) {
|
|
485
|
-
setVizState(pick(['chartType', 'pivotConfig', 'query'], currentState));
|
|
632
|
+
setVizState(pick(['chartType', 'pivotConfig', 'query'], currentState) as QueryBuilderState);
|
|
486
633
|
}
|
|
487
634
|
if (currentState.query && setQuery) {
|
|
488
635
|
setQuery(currentState.query);
|
|
@@ -494,8 +641,8 @@ export default class QueryBuilder extends React.Component {
|
|
|
494
641
|
}
|
|
495
642
|
|
|
496
643
|
finalState.pivotConfig = ResultSet.getNormalizedPivotConfig(
|
|
497
|
-
finalState.query,
|
|
498
|
-
finalState.pivotConfig !== undefined ? finalState.pivotConfig : statePivotConfig
|
|
644
|
+
finalState.query as PivotQuery,
|
|
645
|
+
(finalState.pivotConfig !== undefined ? finalState.pivotConfig : statePivotConfig) as PivotConfig | undefined
|
|
499
646
|
);
|
|
500
647
|
|
|
501
648
|
finalState.missingMembers = this.getMissingMembers(finalState.query, meta);
|
|
@@ -511,7 +658,7 @@ export default class QueryBuilder extends React.Component {
|
|
|
511
658
|
this.setState({
|
|
512
659
|
...finalState,
|
|
513
660
|
queryError: null,
|
|
514
|
-
});
|
|
661
|
+
} as QueryBuilderInternalState);
|
|
515
662
|
|
|
516
663
|
handleVizStateChange(finalState);
|
|
517
664
|
|
|
@@ -542,32 +689,34 @@ export default class QueryBuilder extends React.Component {
|
|
|
542
689
|
});
|
|
543
690
|
}
|
|
544
691
|
} catch (error) {
|
|
692
|
+
const requestError = error as RequestError;
|
|
693
|
+
|
|
545
694
|
this.setState({
|
|
546
|
-
queryError: new Error(
|
|
547
|
-
richQueryError: new Error(
|
|
695
|
+
queryError: new Error(requestError.response?.plainError || requestError.message),
|
|
696
|
+
richQueryError: new Error(requestError.message || requestError.toString())
|
|
548
697
|
});
|
|
549
698
|
}
|
|
550
699
|
}
|
|
551
700
|
|
|
552
|
-
this.setState(finalState, () => handleVizStateChange(this.state));
|
|
701
|
+
this.setState(finalState as QueryBuilderInternalState, () => handleVizStateChange(this.state));
|
|
553
702
|
}
|
|
554
703
|
|
|
555
|
-
validatedQuery(state) {
|
|
704
|
+
validatedQuery(state?: QueryBuilderStateUpdate) {
|
|
556
705
|
const { query } = state || this.state;
|
|
557
706
|
|
|
558
707
|
return validateQuery(query);
|
|
559
708
|
}
|
|
560
709
|
|
|
561
|
-
defaultHeuristics(newState) {
|
|
710
|
+
defaultHeuristics(newState: QueryBuilderStateUpdate): QueryBuilderStateUpdate {
|
|
562
711
|
const { query, sessionGranularity, meta } = this.state;
|
|
563
712
|
|
|
564
|
-
return defaultHeuristics(newState, query, {
|
|
565
|
-
meta,
|
|
713
|
+
return defaultHeuristics(newState as TDefaultHeuristicsState, query, {
|
|
714
|
+
meta: meta as Meta,
|
|
566
715
|
sessionGranularity: sessionGranularity || 'day',
|
|
567
|
-
});
|
|
716
|
+
}) as QueryBuilderStateUpdate;
|
|
568
717
|
}
|
|
569
718
|
|
|
570
|
-
applyStateChangeHeuristics(newState) {
|
|
719
|
+
applyStateChangeHeuristics(newState: QueryBuilderStateUpdate): QueryBuilderStateUpdate {
|
|
571
720
|
const { stateChangeHeuristics, disableHeuristics } = this.props;
|
|
572
721
|
if (disableHeuristics) {
|
|
573
722
|
return newState;
|