@finos/legend-query-builder 3.2.0 → 3.2.2

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.
Files changed (74) hide show
  1. package/lib/__lib__/QueryBuilderColorTheme.d.ts +23 -0
  2. package/lib/__lib__/QueryBuilderColorTheme.d.ts.map +1 -0
  3. package/lib/__lib__/QueryBuilderColorTheme.js +24 -0
  4. package/lib/__lib__/QueryBuilderColorTheme.js.map +1 -0
  5. package/lib/components/QueryBuilder.d.ts.map +1 -1
  6. package/lib/components/QueryBuilder.js +24 -34
  7. package/lib/components/QueryBuilder.js.map +1 -1
  8. package/lib/components/QueryBuilder_LegendApplicationPlugin.d.ts +0 -1
  9. package/lib/components/QueryBuilder_LegendApplicationPlugin.d.ts.map +1 -1
  10. package/lib/components/QueryBuilder_LegendApplicationPlugin.js +3 -4
  11. package/lib/components/QueryBuilder_LegendApplicationPlugin.js.map +1 -1
  12. package/lib/components/QueryLoader.d.ts.map +1 -1
  13. package/lib/components/QueryLoader.js +6 -2
  14. package/lib/components/QueryLoader.js.map +1 -1
  15. package/lib/components/data-access/DataAccessOverview.d.ts +23 -0
  16. package/lib/components/data-access/DataAccessOverview.d.ts.map +1 -0
  17. package/lib/components/data-access/DataAccessOverview.js +146 -0
  18. package/lib/components/data-access/DataAccessOverview.js.map +1 -0
  19. package/lib/components/execution-plan/ExecutionPlanViewer.d.ts.map +1 -1
  20. package/lib/components/execution-plan/ExecutionPlanViewer.js +3 -3
  21. package/lib/components/execution-plan/ExecutionPlanViewer.js.map +1 -1
  22. package/lib/components/execution-plan/SQLExecutionNodeViewer.js +1 -1
  23. package/lib/components/execution-plan/SQLExecutionNodeViewer.js.map +1 -1
  24. package/lib/components/explorer/QueryBuilderPropertySearchPanel.js +1 -1
  25. package/lib/components/explorer/QueryBuilderPropertySearchPanel.js.map +1 -1
  26. package/lib/components/fetch-structure/QueryBuilderResultModifierPanel.d.ts.map +1 -1
  27. package/lib/components/fetch-structure/QueryBuilderResultModifierPanel.js +2 -2
  28. package/lib/components/fetch-structure/QueryBuilderResultModifierPanel.js.map +1 -1
  29. package/lib/components/fetch-structure/QueryBuilderTDSPanel.d.ts +0 -18
  30. package/lib/components/fetch-structure/QueryBuilderTDSPanel.d.ts.map +1 -1
  31. package/lib/components/fetch-structure/QueryBuilderTDSPanel.js +34 -36
  32. package/lib/components/fetch-structure/QueryBuilderTDSPanel.js.map +1 -1
  33. package/lib/components/shared/LambdaEditor.d.ts.map +1 -1
  34. package/lib/components/shared/LambdaEditor.js +7 -19
  35. package/lib/components/shared/LambdaEditor.js.map +1 -1
  36. package/lib/index.css +2 -2
  37. package/lib/index.css.map +1 -1
  38. package/lib/index.d.ts +2 -0
  39. package/lib/index.d.ts.map +1 -1
  40. package/lib/index.js +2 -0
  41. package/lib/index.js.map +1 -1
  42. package/lib/package.json +6 -4
  43. package/lib/stores/QueryBuilderState.d.ts +3 -3
  44. package/lib/stores/QueryBuilderState.d.ts.map +1 -1
  45. package/lib/stores/QueryBuilderState.js +34 -18
  46. package/lib/stores/QueryBuilderState.js.map +1 -1
  47. package/lib/stores/QueryBuilder_LegendApplicationPlugin_Extension.d.ts +9 -0
  48. package/lib/stores/QueryBuilder_LegendApplicationPlugin_Extension.d.ts.map +1 -1
  49. package/lib/stores/data-access/DataAccessState.d.ts +56 -0
  50. package/lib/stores/data-access/DataAccessState.d.ts.map +1 -0
  51. package/lib/stores/data-access/DataAccessState.js +212 -0
  52. package/lib/stores/data-access/DataAccessState.js.map +1 -0
  53. package/lib/stores/entitlements/QueryBuilderCheckEntitlementsState.d.ts +4 -2
  54. package/lib/stores/entitlements/QueryBuilderCheckEntitlementsState.d.ts.map +1 -1
  55. package/lib/stores/entitlements/QueryBuilderCheckEntitlementsState.js +20 -6
  56. package/lib/stores/entitlements/QueryBuilderCheckEntitlementsState.js.map +1 -1
  57. package/package.json +14 -12
  58. package/src/__lib__/QueryBuilderColorTheme.ts +23 -0
  59. package/src/components/QueryBuilder.tsx +85 -96
  60. package/src/components/QueryBuilder_LegendApplicationPlugin.ts +4 -5
  61. package/src/components/QueryLoader.tsx +4 -1
  62. package/src/components/data-access/DataAccessOverview.tsx +308 -0
  63. package/src/components/execution-plan/ExecutionPlanViewer.tsx +1 -3
  64. package/src/components/execution-plan/SQLExecutionNodeViewer.tsx +1 -1
  65. package/src/components/explorer/QueryBuilderPropertySearchPanel.tsx +1 -1
  66. package/src/components/fetch-structure/QueryBuilderResultModifierPanel.tsx +2 -3
  67. package/src/components/fetch-structure/QueryBuilderTDSPanel.tsx +99 -102
  68. package/src/components/shared/LambdaEditor.tsx +4 -21
  69. package/src/index.ts +4 -0
  70. package/src/stores/QueryBuilderState.ts +65 -19
  71. package/src/stores/QueryBuilder_LegendApplicationPlugin_Extension.ts +10 -0
  72. package/src/stores/data-access/DataAccessState.ts +322 -0
  73. package/src/stores/entitlements/QueryBuilderCheckEntitlementsState.ts +53 -6
  74. package/tsconfig.json +3 -0
@@ -0,0 +1,308 @@
1
+ /**
2
+ * Copyright (c) 2020-present, Goldman Sachs
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { useEffect } from 'react';
18
+ import { observer } from 'mobx-react-lite';
19
+ import { useApplicationStore } from '@finos/legend-application';
20
+ import type {
21
+ DataAccessState,
22
+ DatasetAccessInfo,
23
+ } from '../../stores/data-access/DataAccessState.js';
24
+ import {
25
+ CheckCircleIcon,
26
+ ExclamationCircleIcon,
27
+ MinusCircleIcon,
28
+ PanelLoadingIndicator,
29
+ RefreshIcon,
30
+ TimesCircleIcon,
31
+ clsx,
32
+ } from '@finos/legend-art';
33
+ import {
34
+ DataGrid,
35
+ type DataGridCellRendererParams,
36
+ } from '@finos/legend-lego/data-grid';
37
+ import {
38
+ DatasetEntitlementAccessApprovedReport,
39
+ DatasetEntitlementAccessGrantedReport,
40
+ DatasetEntitlementAccessNotGrantedReport,
41
+ DatasetEntitlementAccessRequestedReport,
42
+ DatasetEntitlementUnsupportedReport,
43
+ } from '@finos/legend-graph';
44
+ import { Doughnut } from 'react-chartjs-2';
45
+ import { getNullableFirstEntry } from '@finos/legend-shared';
46
+ import type { QueryBuilder_LegendApplicationPlugin_Extension } from '../../stores/QueryBuilder_LegendApplicationPlugin_Extension.js';
47
+
48
+ const DataAccessOverviewChart = observer(
49
+ (props: { dataAccessState: DataAccessState }) => {
50
+ const { dataAccessState } = props;
51
+ const applicationStore = useApplicationStore();
52
+ const entitlementCheckInfo = dataAccessState.entitlementCheckInfo;
53
+ const total = entitlementCheckInfo.total;
54
+ const accessGrantedCount =
55
+ getNullableFirstEntry(entitlementCheckInfo.data)?.count ?? 0;
56
+ const accessGrantedPercentage =
57
+ getNullableFirstEntry(entitlementCheckInfo.data)?.percentage ?? 0;
58
+
59
+ return (
60
+ <div className="data-access-overview__chart">
61
+ <div className="data-access-overview__chart__actions">
62
+ <button
63
+ className="data-access-overview__chart__actions__refresh-btn btn--dark"
64
+ tabIndex={-1}
65
+ title="Refresh"
66
+ onClick={() => {
67
+ dataAccessState
68
+ .refresh()
69
+ .catch(applicationStore.alertUnhandledError);
70
+ }}
71
+ >
72
+ <RefreshIcon />
73
+ </button>
74
+ </div>
75
+ <div className="data-access-overview__chart__container">
76
+ <Doughnut
77
+ data={{
78
+ labels: entitlementCheckInfo.data.map((item) => item.label),
79
+ datasets: [
80
+ {
81
+ data: entitlementCheckInfo.data.map((item) => item.count),
82
+ backgroundColor: entitlementCheckInfo.data.map(
83
+ (item) => item.color,
84
+ ),
85
+ hoverBorderWidth: 0,
86
+ borderWidth: 0,
87
+ },
88
+ ],
89
+ }}
90
+ options={{
91
+ responsive: true,
92
+ resizeDelay: 0,
93
+ maintainAspectRatio: false,
94
+ cutout: '75%',
95
+ plugins: {
96
+ tooltip: {
97
+ enabled: total !== 0,
98
+ usePointStyle: false,
99
+ boxPadding: 5,
100
+ callbacks: {
101
+ labelPointStyle: () => ({
102
+ pointStyle: 'rectRounded',
103
+ rotation: 0,
104
+ }),
105
+ },
106
+ },
107
+ },
108
+ }}
109
+ />
110
+ <div className="data-access-overview__chart__stats">
111
+ <div className="data-access-overview__chart__stats__percentage">
112
+ {accessGrantedPercentage}%
113
+ </div>
114
+ <div className="data-access-overview__chart__stats__tally">
115
+ {total === 0 ? 0 : accessGrantedCount}/{total}
116
+ </div>
117
+ </div>
118
+ </div>
119
+ </div>
120
+ );
121
+ },
122
+ );
123
+
124
+ const AccessStatusCellRenderer = observer(
125
+ (
126
+ params: DataGridCellRendererParams<DatasetAccessInfo> & {
127
+ dataAccessState: DataAccessState;
128
+ },
129
+ ) => {
130
+ const { data } = params;
131
+ const applicationStore = useApplicationStore();
132
+
133
+ if (!data) {
134
+ return null;
135
+ }
136
+
137
+ if (
138
+ data.entitlementReport instanceof DatasetEntitlementAccessGrantedReport
139
+ ) {
140
+ return (
141
+ <div className="data-access-overview__grid__access-status-cell">
142
+ <div className="data-access-overview__grid__access-status-cell__content">
143
+ <div className="data-access-overview__grid__access-status-cell__icon data-access-overview__grid__access-status-cell__icon--access-granted">
144
+ <CheckCircleIcon />
145
+ </div>
146
+ <div className="data-access-overview__grid__access-status-cell__text">
147
+ Access Granted
148
+ </div>
149
+ </div>
150
+ </div>
151
+ );
152
+ } else if (
153
+ data.entitlementReport instanceof DatasetEntitlementAccessApprovedReport
154
+ ) {
155
+ return (
156
+ <div className="data-access-overview__grid__access-status-cell">
157
+ <div className="data-access-overview__grid__access-status-cell__content">
158
+ <div className="data-access-overview__grid__access-status-cell__icon data-access-overview__grid__access-status-cell__icon--access-approved">
159
+ <MinusCircleIcon />
160
+ </div>
161
+ <div className="data-access-overview__grid__access-status-cell__text">
162
+ Access Approved
163
+ </div>
164
+ </div>
165
+ </div>
166
+ );
167
+ } else if (
168
+ data.entitlementReport instanceof DatasetEntitlementAccessRequestedReport
169
+ ) {
170
+ return (
171
+ <div className="data-access-overview__grid__access-status-cell">
172
+ <div className="data-access-overview__grid__access-status-cell__content">
173
+ <div className="data-access-overview__grid__access-status-cell__icon data-access-overview__grid__access-status-cell__icon--access-requested">
174
+ <ExclamationCircleIcon />
175
+ </div>
176
+ <div className="data-access-overview__grid__access-status-cell__text">
177
+ Access Requested
178
+ </div>
179
+ </div>
180
+ </div>
181
+ );
182
+ } else if (
183
+ data.entitlementReport instanceof DatasetEntitlementAccessNotGrantedReport
184
+ ) {
185
+ const plugins = applicationStore.pluginManager
186
+ .getApplicationPlugins()
187
+ .flatMap(
188
+ (plugin) =>
189
+ (
190
+ plugin as QueryBuilder_LegendApplicationPlugin_Extension
191
+ ).getExtraDatasetEntitlementAccessNotGrantedReportActionConfigurations?.() ??
192
+ [],
193
+ );
194
+ let action: React.ReactNode | undefined;
195
+ for (const plugin of plugins) {
196
+ action = plugin.renderer(data);
197
+ if (action) {
198
+ break;
199
+ }
200
+ }
201
+ return (
202
+ <div className="data-access-overview__grid__access-status-cell">
203
+ <div className="data-access-overview__grid__access-status-cell__content">
204
+ <div className="data-access-overview__grid__access-status-cell__icon data-access-overview__grid__access-status-cell__icon--access-not-granted">
205
+ <TimesCircleIcon />
206
+ </div>
207
+ <div className="data-access-overview__grid__access-status-cell__text">
208
+ Access Not Granted
209
+ </div>
210
+ </div>
211
+ {action && (
212
+ <div className="data-access-overview__grid__access-status-cell__action">
213
+ {action}
214
+ </div>
215
+ )}
216
+ </div>
217
+ );
218
+ } else if (
219
+ data.entitlementReport instanceof DatasetEntitlementUnsupportedReport
220
+ ) {
221
+ return (
222
+ <div className="data-access-overview__grid__empty-cell">
223
+ (unsupported)
224
+ </div>
225
+ );
226
+ }
227
+
228
+ return null;
229
+ },
230
+ );
231
+
232
+ const DataAccessOverviewGrid = observer(
233
+ (props: { dataAccessState: DataAccessState }) => {
234
+ const { dataAccessState } = props;
235
+
236
+ return (
237
+ <div className="data-access-overview__grid ag-theme-balham-dark">
238
+ <DataGrid
239
+ rowData={dataAccessState.datasets}
240
+ gridOptions={{
241
+ suppressScrollOnNewData: true,
242
+ getRowId: (rowData) => rowData.data.uuid,
243
+ }}
244
+ columnDefs={[
245
+ {
246
+ minWidth: 50,
247
+ sortable: true,
248
+ resizable: true,
249
+ field: 'specification.name',
250
+ headerName: 'Dataset',
251
+ flex: 1,
252
+ },
253
+ {
254
+ minWidth: 50,
255
+ sortable: true,
256
+ resizable: true,
257
+ field: 'specification.type',
258
+ headerName: 'Type',
259
+ flex: 1,
260
+ },
261
+ {
262
+ minWidth: 50,
263
+ sortable: true,
264
+ resizable: true,
265
+ headerName: 'Access Status',
266
+ cellRendererParams: {
267
+ dataAccessState,
268
+ },
269
+ cellRenderer: AccessStatusCellRenderer,
270
+ flex: 1,
271
+ },
272
+ ]}
273
+ />
274
+ </div>
275
+ );
276
+ },
277
+ );
278
+
279
+ export const DataAccessOverview = observer(
280
+ (props: {
281
+ dataAccessState: DataAccessState;
282
+ compact?: boolean | undefined;
283
+ }) => {
284
+ const { dataAccessState, compact } = props;
285
+ const applicationStore = useApplicationStore();
286
+
287
+ useEffect(() => {
288
+ dataAccessState.intialize().catch(applicationStore.alertUnhandledError);
289
+ }, [applicationStore, dataAccessState]);
290
+
291
+ return (
292
+ <div
293
+ className={clsx('data-access-overview', {
294
+ 'data-access-overview--compact': Boolean(compact),
295
+ })}
296
+ >
297
+ <PanelLoadingIndicator
298
+ isLoading={
299
+ dataAccessState.surveyDatasetsState.isInProgress ||
300
+ dataAccessState.checkEntitlementsState.isInProgress
301
+ }
302
+ />
303
+ <DataAccessOverviewChart dataAccessState={dataAccessState} />
304
+ <DataAccessOverviewGrid dataAccessState={dataAccessState} />
305
+ </div>
306
+ );
307
+ },
308
+ );
@@ -380,7 +380,7 @@ const ExecutionPlanViewPanel = observer(
380
380
  inputValue={displayData}
381
381
  isReadOnly={true}
382
382
  language={CODE_EDITOR_LANGUAGE.JSON}
383
- showMiniMap={false}
383
+ hideMinimap={true}
384
384
  />
385
385
  )}
386
386
  {executionPlanState.viewMode ===
@@ -462,7 +462,6 @@ const ExecutionPlanViewerContent = observer(
462
462
  inputValue={JSON.stringify(rawPlan, undefined, DEFAULT_TAB_SIZE)}
463
463
  isReadOnly={true}
464
464
  language={CODE_EDITOR_LANGUAGE.JSON}
465
- showMiniMap={true}
466
465
  />
467
466
  )}
468
467
  </div>
@@ -523,7 +522,6 @@ export const ExecutionPlanViewer = observer(
523
522
  inputValue={executionPlanState.debugText}
524
523
  isReadOnly={true}
525
524
  language={CODE_EDITOR_LANGUAGE.TEXT}
526
- showMiniMap={true}
527
525
  />
528
526
  </PanelContent>
529
527
  </div>
@@ -42,7 +42,7 @@ export const SQLExecutionNodeViewer: React.FC<{
42
42
  inputValue={formatSQL(query)}
43
43
  isReadOnly={true}
44
44
  language={CODE_EDITOR_LANGUAGE.SQL}
45
- showMiniMap={false}
45
+ hideMinimap={true}
46
46
  />
47
47
  );
48
48
  });
@@ -14,7 +14,7 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import React, { useMemo, useRef, useState } from 'react';
17
+ import { useMemo, useRef, useState } from 'react';
18
18
  import {
19
19
  clsx,
20
20
  CheckSquareIcon,
@@ -30,6 +30,7 @@ import {
30
30
  DropdownMenu,
31
31
  MenuContent,
32
32
  MenuContentItem,
33
+ ModalFooterButton,
33
34
  } from '@finos/legend-art';
34
35
  import {
35
36
  COLUMN_SORT_TYPE,
@@ -262,9 +263,7 @@ export const QueryResultModifierModal = observer(
262
263
  </div>
263
264
  </ModalBody>
264
265
  <ModalFooter>
265
- <button className="btn modal__footer__close-btn" onClick={close}>
266
- Close
267
- </button>
266
+ <ModalFooterButton onClick={close} text="Close" />
268
267
  </ModalFooter>
269
268
  </Modal>
270
269
  </Dialog>
@@ -39,8 +39,8 @@ import {
39
39
  CalendarIcon,
40
40
  CalendarClockIcon,
41
41
  CustomSelectorInput,
42
- PURE_FunctionIcon,
43
42
  PanelEntryDropZonePlaceholder,
43
+ FunctionIcon,
44
44
  } from '@finos/legend-art';
45
45
  import {
46
46
  type QueryBuilderExplorerTreeDragSource,
@@ -102,62 +102,6 @@ import { getPropertyChainName } from '../../stores/QueryBuilderPropertyEditorSta
102
102
  import { generateDefaultValueForPrimitiveType } from '../../stores/QueryBuilderValueSpecificationHelper.js';
103
103
  import { QUERY_BUILDER_CALENDAR_TYPE } from '../../graph-manager/QueryBuilderConst.js';
104
104
 
105
- export type CalendarFunctionOption = {
106
- label: string | React.ReactNode;
107
- value: QueryBuilderAggregateCalendarFunction;
108
- };
109
-
110
- export const buildCalendarFunctionOption = (
111
- calendarFunction: QueryBuilderAggregateCalendarFunction,
112
- ): CalendarFunctionOption => ({
113
- label: (
114
- <div
115
- className="query-builder__projection__calendar__function__label"
116
- title={calendarFunction.getLabel()}
117
- >
118
- <PURE_FunctionIcon />
119
- <div className="query-builder__projection__calendar__function__label__title">
120
- {calendarFunction.getLabel()}
121
- </div>
122
- </div>
123
- ),
124
- value: calendarFunction,
125
- });
126
-
127
- export type CalendarFunctionDateColumnOption = {
128
- label: string;
129
- value: AbstractPropertyExpression;
130
- };
131
-
132
- export const buildCalendarFunctionDateColumnOption = (
133
- dateColumn: Property | AbstractPropertyExpression,
134
- parameter: VariableExpression,
135
- humanizeLabel: boolean,
136
- ): CalendarFunctionDateColumnOption => {
137
- if (dateColumn instanceof Property) {
138
- const propertyExpression = new AbstractPropertyExpression('');
139
- propertyExpression_setFunc(
140
- propertyExpression,
141
- PropertyExplicitReference.create(guaranteeNonNullable(dateColumn)),
142
- );
143
- propertyExpression.parametersValues = [parameter];
144
- return {
145
- label: getPropertyChainName(propertyExpression, humanizeLabel),
146
- value: propertyExpression,
147
- };
148
- } else {
149
- return {
150
- label: getPropertyChainName(dateColumn, humanizeLabel),
151
- value: dateColumn,
152
- };
153
- }
154
- };
155
-
156
- export type CalendarTypeOption = {
157
- label: string;
158
- value: QUERY_BUILDER_CALENDAR_TYPE;
159
- };
160
-
161
105
  const QueryBuilderProjectionColumnContextMenu = observer(
162
106
  forwardRef<
163
107
  HTMLDivElement,
@@ -304,6 +248,76 @@ const QueryBuilderDerivationProjectionColumnEditor = observer(
304
248
  },
305
249
  );
306
250
 
251
+ type CalendarFunctionOption = {
252
+ label: string | React.ReactNode;
253
+ value: QueryBuilderAggregateCalendarFunction;
254
+ };
255
+
256
+ const buildCalendarFunctionOption = (
257
+ calendarFunction: QueryBuilderAggregateCalendarFunction,
258
+ ): CalendarFunctionOption => ({
259
+ label: (
260
+ <div
261
+ className="query-builder__projection__calendar__function__label"
262
+ title={calendarFunction.getLabel()}
263
+ >
264
+ <FunctionIcon className="query-builder__projection__calendar__function__label__icon" />
265
+ <div className="query-builder__projection__calendar__function__label__title">
266
+ {calendarFunction.getLabel()}
267
+ </div>
268
+ </div>
269
+ ),
270
+ value: calendarFunction,
271
+ });
272
+
273
+ type CalendarFunctionDateColumnOption = {
274
+ label: React.ReactNode;
275
+ value: AbstractPropertyExpression;
276
+ };
277
+
278
+ const buildCalendarFunctionDateColumnOption = (
279
+ dateColumn: Property | AbstractPropertyExpression,
280
+ parameter: VariableExpression,
281
+ humanizeLabel: boolean,
282
+ ): CalendarFunctionDateColumnOption => {
283
+ if (dateColumn instanceof Property) {
284
+ const propertyExpression = new AbstractPropertyExpression('');
285
+ propertyExpression_setFunc(
286
+ propertyExpression,
287
+ PropertyExplicitReference.create(guaranteeNonNullable(dateColumn)),
288
+ );
289
+ propertyExpression.parametersValues = [parameter];
290
+ return {
291
+ label: getPropertyChainName(propertyExpression, humanizeLabel),
292
+ value: propertyExpression,
293
+ };
294
+ } else {
295
+ return {
296
+ label: getPropertyChainName(dateColumn, humanizeLabel),
297
+ value: dateColumn,
298
+ };
299
+ }
300
+ };
301
+
302
+ type CalendarTypeOption = {
303
+ label: React.ReactNode;
304
+ value: QUERY_BUILDER_CALENDAR_TYPE;
305
+ };
306
+
307
+ const buildCalendarTypeOption = (
308
+ val: QUERY_BUILDER_CALENDAR_TYPE,
309
+ ): CalendarTypeOption => ({
310
+ label: (
311
+ <div className="query-builder__projection__calendar__type__option">
312
+ <CalendarIcon className="query-builder__projection__calendar__type__option__icon" />
313
+ <div className="query-builder__projection__calendar__type__option__title">
314
+ {val}
315
+ </div>
316
+ </div>
317
+ ),
318
+ value: val,
319
+ });
320
+
307
321
  const QueryBuilderProjectionColumnEditor = observer(
308
322
  (props: { projectionColumnState: QueryBuilderProjectionColumnState }) => {
309
323
  const handleRef = useRef<HTMLDivElement>(null);
@@ -369,31 +383,13 @@ const QueryBuilderProjectionColumnEditor = observer(
369
383
  ),
370
384
  );
371
385
  const calendarTypeOptions = Object.values(QUERY_BUILDER_CALENDAR_TYPE).map(
372
- (ct) => ({
373
- label: (
374
- <div className="query-builder__projection__calendar__type__option">
375
- <CalendarIcon />
376
- <div className="query-builder__projection__calendar__type__option__title">
377
- {ct}
378
- </div>
379
- </div>
380
- ),
381
- value: ct,
382
- }),
386
+ buildCalendarTypeOption,
383
387
  );
384
388
  const selectedCalendarTypeOption = aggregateColumnState?.calendarFunction
385
389
  ?.calendarType
386
- ? {
387
- label: (
388
- <div className="query-builder__projection__calendar__type__option">
389
- <CalendarIcon />
390
- <div className="query-builder__projection__calendar__type__option__title">
391
- {aggregateColumnState.calendarFunction.calendarType}
392
- </div>
393
- </div>
394
- ),
395
- value: aggregateColumnState.calendarFunction.calendarType,
396
- }
390
+ ? buildCalendarTypeOption(
391
+ aggregateColumnState.calendarFunction.calendarType,
392
+ )
397
393
  : null;
398
394
  const onCalendarTypeOptionChange = (option: CalendarTypeOption): void => {
399
395
  if (
@@ -661,13 +657,15 @@ const QueryBuilderProjectionColumnEditor = observer(
661
657
  aggregateColumnState &&
662
658
  aggregateCalendarFunctions.length > 0 && (
663
659
  <div
664
- className={
665
- aggregateColumnState.hideCalendarColumnState
666
- ? 'query-builder__projection__column__aggregate__calendar--clock--icon__hidden'
667
- : 'query-builder__projection__column__aggregate__calendar--clock--icon'
668
- }
660
+ className={clsx(
661
+ 'query-builder__projection__column__aggregate__calendar__toggler',
662
+ {
663
+ 'query-builder__projection__column__aggregate__calendar__toggler--active':
664
+ !aggregateColumnState.hideCalendarColumnState,
665
+ },
666
+ )}
669
667
  onClick={toggleHideCalendarColumnState}
670
- title="Click to select calendar function"
668
+ title="Toggle calendar aggregation"
671
669
  >
672
670
  <CalendarClockIcon />
673
671
  </div>
@@ -749,21 +747,19 @@ const QueryBuilderProjectionColumnEditor = observer(
749
747
  },
750
748
  )}
751
749
  >
752
- <div data-testid="test">
753
- <CustomSelectorInput
754
- className="query-builder__projection__calendar__function"
755
- options={calendarFunctionOptions}
756
- onChange={onCalendarFunctionOptionChange}
757
- value={selectedCalendarFunctionOption}
758
- placeholder={'Select Calendar Function'}
759
- isClearable={true}
760
- escapeClearsValue={true}
761
- darkMode={
762
- !applicationStore.layoutService
763
- .TEMPORARY__isLightColorThemeEnabled
764
- }
765
- />
766
- </div>
750
+ <CustomSelectorInput
751
+ className="query-builder__projection__calendar__function"
752
+ options={calendarFunctionOptions}
753
+ onChange={onCalendarFunctionOptionChange}
754
+ value={selectedCalendarFunctionOption}
755
+ placeholder="Select Calendar Function"
756
+ isClearable={true}
757
+ escapeClearsValue={true}
758
+ darkMode={
759
+ !applicationStore.layoutService
760
+ .TEMPORARY__isLightColorThemeEnabled
761
+ }
762
+ />
767
763
  <div className="query-builder__projection__calendar__value">
768
764
  <BasicValueSpecificationEditor
769
765
  valueSpecification={
@@ -807,7 +803,7 @@ const QueryBuilderProjectionColumnEditor = observer(
807
803
  </div>
808
804
  </div>
809
805
  ) : (
810
- <div className="query-builder__projection__calendar__date__column__dnd__placeholder">
806
+ <div className="query-builder__projection__calendar__date__column__placeholder">
811
807
  Drag and drop date column here
812
808
  </div>
813
809
  )}
@@ -818,7 +814,8 @@ const QueryBuilderProjectionColumnEditor = observer(
818
814
  options={calendarTypeOptions}
819
815
  onChange={onCalendarTypeOptionChange}
820
816
  value={selectedCalendarTypeOption ?? calendarTypeOptions[0]}
821
- placeholder={'Select calendar type'}
817
+ placeholder="Select calendar type"
818
+ disabled={!aggregateColumnState.calendarFunction}
822
819
  darkMode={
823
820
  !applicationStore.layoutService
824
821
  .TEMPORARY__isLightColorThemeEnabled