@object-ui/plugin-dashboard 3.1.5 → 3.3.1

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 (84) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +21 -1
  3. package/dist/index.d.ts +1 -1
  4. package/dist/index.js +1162 -939
  5. package/dist/index.umd.cjs +4 -4
  6. package/dist/packages/plugin-dashboard/src/DashboardConfigPanel.d.ts.map +1 -0
  7. package/dist/packages/plugin-dashboard/src/DashboardConfigPanel.stories.d.ts.map +1 -0
  8. package/dist/packages/plugin-dashboard/src/DashboardGridLayout.d.ts.map +1 -0
  9. package/dist/{src → packages/plugin-dashboard/src}/DashboardRenderer.d.ts +5 -0
  10. package/dist/packages/plugin-dashboard/src/DashboardRenderer.d.ts.map +1 -0
  11. package/dist/packages/plugin-dashboard/src/DashboardRenderer.stories.d.ts.map +1 -0
  12. package/dist/packages/plugin-dashboard/src/DashboardWithConfig.d.ts.map +1 -0
  13. package/dist/{src → packages/plugin-dashboard/src}/MetricCard.d.ts +4 -0
  14. package/dist/packages/plugin-dashboard/src/MetricCard.d.ts.map +1 -0
  15. package/dist/packages/plugin-dashboard/src/MetricWidget.d.ts +31 -0
  16. package/dist/packages/plugin-dashboard/src/MetricWidget.d.ts.map +1 -0
  17. package/dist/packages/plugin-dashboard/src/ObjectDataTable.d.ts.map +1 -0
  18. package/dist/packages/plugin-dashboard/src/ObjectMetricWidget.d.ts +59 -0
  19. package/dist/packages/plugin-dashboard/src/ObjectMetricWidget.d.ts.map +1 -0
  20. package/dist/packages/plugin-dashboard/src/ObjectPivotTable.d.ts.map +1 -0
  21. package/dist/packages/plugin-dashboard/src/PivotTable.d.ts.map +1 -0
  22. package/dist/packages/plugin-dashboard/src/WidgetConfigPanel.d.ts.map +1 -0
  23. package/dist/{src → packages/plugin-dashboard/src}/index.d.ts +4 -2
  24. package/dist/packages/plugin-dashboard/src/index.d.ts.map +1 -0
  25. package/dist/packages/plugin-dashboard/src/utils.d.ts.map +1 -0
  26. package/package.json +44 -11
  27. package/.turbo/turbo-build.log +0 -34
  28. package/dist/src/DashboardConfigPanel.d.ts.map +0 -1
  29. package/dist/src/DashboardConfigPanel.stories.d.ts.map +0 -1
  30. package/dist/src/DashboardGridLayout.d.ts.map +0 -1
  31. package/dist/src/DashboardRenderer.d.ts.map +0 -1
  32. package/dist/src/DashboardRenderer.stories.d.ts.map +0 -1
  33. package/dist/src/DashboardWithConfig.d.ts.map +0 -1
  34. package/dist/src/MetricCard.d.ts.map +0 -1
  35. package/dist/src/MetricWidget.d.ts +0 -24
  36. package/dist/src/MetricWidget.d.ts.map +0 -1
  37. package/dist/src/ObjectDataTable.d.ts.map +0 -1
  38. package/dist/src/ObjectPivotTable.d.ts.map +0 -1
  39. package/dist/src/PivotTable.d.ts.map +0 -1
  40. package/dist/src/WidgetConfigPanel.d.ts.map +0 -1
  41. package/dist/src/index.d.ts.map +0 -1
  42. package/dist/src/utils.d.ts.map +0 -1
  43. package/src/DashboardConfigPanel.stories.tsx +0 -164
  44. package/src/DashboardConfigPanel.tsx +0 -158
  45. package/src/DashboardGridLayout.tsx +0 -367
  46. package/src/DashboardRenderer.stories.tsx +0 -173
  47. package/src/DashboardRenderer.tsx +0 -445
  48. package/src/DashboardWithConfig.tsx +0 -211
  49. package/src/MetricCard.tsx +0 -82
  50. package/src/MetricWidget.tsx +0 -76
  51. package/src/ObjectDataTable.tsx +0 -226
  52. package/src/ObjectPivotTable.tsx +0 -160
  53. package/src/PivotTable.tsx +0 -262
  54. package/src/WidgetConfigPanel.tsx +0 -540
  55. package/src/__tests__/DashboardConfigPanel.test.tsx +0 -206
  56. package/src/__tests__/DashboardGridLayout.test.tsx +0 -199
  57. package/src/__tests__/DashboardRenderer.autoRefresh.test.tsx +0 -124
  58. package/src/__tests__/DashboardRenderer.designMode.test.tsx +0 -386
  59. package/src/__tests__/DashboardRenderer.header.test.tsx +0 -114
  60. package/src/__tests__/DashboardRenderer.mobile.test.tsx +0 -214
  61. package/src/__tests__/DashboardRenderer.widgetData.test.tsx +0 -1283
  62. package/src/__tests__/DashboardWithConfig.test.tsx +0 -276
  63. package/src/__tests__/MetricCard.test.tsx +0 -82
  64. package/src/__tests__/ObjectDataTable.test.tsx +0 -211
  65. package/src/__tests__/ObjectPivotTable.test.tsx +0 -192
  66. package/src/__tests__/PivotTable.test.tsx +0 -162
  67. package/src/__tests__/WidgetConfigPanel.test.tsx +0 -492
  68. package/src/__tests__/ensureWidgetIds.test.tsx +0 -103
  69. package/src/index.tsx +0 -214
  70. package/src/utils.ts +0 -17
  71. package/tsconfig.json +0 -19
  72. package/vite.config.ts +0 -63
  73. package/vitest.config.ts +0 -9
  74. package/vitest.setup.tsx +0 -18
  75. /package/dist/{src → packages/plugin-dashboard/src}/DashboardConfigPanel.d.ts +0 -0
  76. /package/dist/{src → packages/plugin-dashboard/src}/DashboardConfigPanel.stories.d.ts +0 -0
  77. /package/dist/{src → packages/plugin-dashboard/src}/DashboardGridLayout.d.ts +0 -0
  78. /package/dist/{src → packages/plugin-dashboard/src}/DashboardRenderer.stories.d.ts +0 -0
  79. /package/dist/{src → packages/plugin-dashboard/src}/DashboardWithConfig.d.ts +0 -0
  80. /package/dist/{src → packages/plugin-dashboard/src}/ObjectDataTable.d.ts +0 -0
  81. /package/dist/{src → packages/plugin-dashboard/src}/ObjectPivotTable.d.ts +0 -0
  82. /package/dist/{src → packages/plugin-dashboard/src}/PivotTable.d.ts +0 -0
  83. /package/dist/{src → packages/plugin-dashboard/src}/WidgetConfigPanel.d.ts +0 -0
  84. /package/dist/{src → packages/plugin-dashboard/src}/utils.d.ts +0 -0
@@ -1,540 +0,0 @@
1
- /**
2
- * ObjectUI
3
- * Copyright (c) 2024-present ObjectStack Inc.
4
- *
5
- * This source code is licensed under the MIT license found in the
6
- * LICENSE file in the root directory of this source tree.
7
- */
8
-
9
- import * as React from 'react';
10
- import {
11
- ConfigPanelRenderer,
12
- useConfigDraft,
13
- Combobox,
14
- } from '@object-ui/components';
15
- import { ConfigRow } from '@object-ui/components';
16
- import type { ConfigPanelSchema, ConfigField } from '@object-ui/components';
17
-
18
- // ---------------------------------------------------------------------------
19
- // Widget type options derived from @object-ui/types DASHBOARD_WIDGET_TYPES
20
- // ---------------------------------------------------------------------------
21
-
22
- const WIDGET_TYPE_OPTIONS = [
23
- { value: 'metric', label: 'Metric' },
24
- { value: 'bar', label: 'Bar Chart' },
25
- { value: 'line', label: 'Line Chart' },
26
- { value: 'pie', label: 'Pie Chart' },
27
- { value: 'donut', label: 'Donut Chart' },
28
- { value: 'area', label: 'Area Chart' },
29
- { value: 'scatter', label: 'Scatter Plot' },
30
- { value: 'table', label: 'Table' },
31
- { value: 'pivot', label: 'Pivot Table' },
32
- { value: 'list', label: 'List' },
33
- { value: 'custom', label: 'Custom' },
34
- ];
35
-
36
- const COLOR_VARIANT_OPTIONS = [
37
- { value: 'default', label: 'Default' },
38
- { value: 'blue', label: 'Blue' },
39
- { value: 'teal', label: 'Teal' },
40
- { value: 'orange', label: 'Orange' },
41
- { value: 'purple', label: 'Purple' },
42
- { value: 'success', label: 'Success' },
43
- { value: 'warning', label: 'Warning' },
44
- { value: 'danger', label: 'Danger' },
45
- ];
46
-
47
- const AGGREGATE_OPTIONS = [
48
- { value: 'count', label: 'Count' },
49
- { value: 'sum', label: 'Sum' },
50
- { value: 'avg', label: 'Average' },
51
- { value: 'min', label: 'Min' },
52
- { value: 'max', label: 'Max' },
53
- ];
54
-
55
- // ---------------------------------------------------------------------------
56
- // Helpers
57
- // ---------------------------------------------------------------------------
58
-
59
- const CHART_TYPES = ['bar', 'line', 'area', 'pie', 'donut', 'scatter'];
60
-
61
- function isChartType(t: string | undefined): boolean {
62
- return !!t && CHART_TYPES.includes(t);
63
- }
64
-
65
- const SORT_BY_OPTIONS = [
66
- { value: 'group', label: 'Group' },
67
- { value: 'value', label: 'Value' },
68
- ];
69
-
70
- const SORT_ORDER_OPTIONS = [
71
- { value: 'asc', label: '↑' },
72
- { value: 'desc', label: '↓' },
73
- ];
74
-
75
- // ---------------------------------------------------------------------------
76
- // Schema builder — creates a ConfigPanelSchema with dynamic options for
77
- // object/field selectors when metadata is available. Sections are shown or
78
- // hidden based on the current widget type via `visibleWhen`.
79
- // ---------------------------------------------------------------------------
80
-
81
- export type SelectOption = { value: string; label: string };
82
-
83
- function buildFieldCombobox(
84
- key: string,
85
- label: string,
86
- placeholder: string,
87
- availableFields: SelectOption[] | undefined,
88
- ): ConfigField {
89
- return {
90
- key,
91
- label,
92
- type: 'custom',
93
- render: (value: any, onChange: (v: any) => void, draft: Record<string, any>) => (
94
- <ConfigRow label={label}>
95
- <div data-testid={`config-field-${key}`}>
96
- <Combobox
97
- options={availableFields ?? []}
98
- value={value ?? ''}
99
- onValueChange={onChange}
100
- placeholder={placeholder}
101
- searchPlaceholder="Search fields…"
102
- emptyText="No fields found."
103
- className="h-7 w-32 text-xs"
104
- disabled={!draft.object}
105
- />
106
- </div>
107
- </ConfigRow>
108
- ),
109
- };
110
- }
111
-
112
- function buildWidgetSchema(
113
- availableObjects?: SelectOption[],
114
- availableFields?: SelectOption[],
115
- widgetType?: string,
116
- ): ConfigPanelSchema {
117
- const hasObjects = availableObjects && availableObjects.length > 0;
118
-
119
- const objectField: ConfigField = hasObjects
120
- ? {
121
- key: 'object',
122
- label: 'Data source',
123
- type: 'custom',
124
- render: (value: any, onChange: (v: any) => void) => (
125
- <ConfigRow label="Data source">
126
- <div data-testid="config-field-object">
127
- <Combobox
128
- options={availableObjects}
129
- value={value ?? ''}
130
- onValueChange={onChange}
131
- placeholder="Select object…"
132
- searchPlaceholder="Search objects…"
133
- emptyText="No objects found."
134
- className="h-7 w-32 text-xs"
135
- />
136
- </div>
137
- </ConfigRow>
138
- ),
139
- }
140
- : {
141
- key: 'object',
142
- label: 'Data source',
143
- type: 'input',
144
- placeholder: 'Object name',
145
- };
146
-
147
- // --- Chart-specific field selectors (category / value / aggregate) --------
148
-
149
- const categoryFieldDef: ConfigField = hasObjects
150
- ? buildFieldCombobox('categoryField', 'Category field', 'Select field…', availableFields)
151
- : {
152
- key: 'categoryField',
153
- label: 'Category field',
154
- type: 'input',
155
- placeholder: 'e.g. status',
156
- };
157
-
158
- const valueFieldDef: ConfigField = hasObjects
159
- ? buildFieldCombobox('valueField', 'Value field', 'Select field…', availableFields)
160
- : {
161
- key: 'valueField',
162
- label: 'Value field',
163
- type: 'input',
164
- placeholder: 'e.g. amount',
165
- };
166
-
167
- // --- Pivot-specific field selectors (row / column / value) ----------------
168
-
169
- const pivotRowField: ConfigField = hasObjects
170
- ? buildFieldCombobox('rowField', 'Field', 'Select row field…', availableFields)
171
- : { key: 'rowField', label: 'Field', type: 'input', placeholder: 'e.g. owner' };
172
-
173
- const pivotColumnField: ConfigField = hasObjects
174
- ? buildFieldCombobox('columnField', 'Field', 'Select column field…', availableFields)
175
- : { key: 'columnField', label: 'Field', type: 'input', placeholder: 'e.g. stage' };
176
-
177
- const pivotValueField: ConfigField = hasObjects
178
- ? buildFieldCombobox('pivotValueField', 'Field', 'Select value field…', availableFields)
179
- : { key: 'pivotValueField', label: 'Field', type: 'input', placeholder: 'e.g. amount' };
180
-
181
- // ---- Breadcrumb varies by widget type ------------------------------------
182
-
183
- const BREADCRUMB_LABELS: Record<string, string> = {
184
- pivot: 'Pivot table',
185
- table: 'Table',
186
- };
187
- const typeName = BREADCRUMB_LABELS[widgetType ?? '']
188
- ?? (isChartType(widgetType) ? 'Chart' : 'Widget');
189
-
190
- return {
191
- breadcrumb: ['Dashboard', typeName],
192
- sections: [
193
- // ----- General (always visible) -------------------------------------
194
- {
195
- key: 'general',
196
- title: 'General',
197
- fields: [
198
- {
199
- key: 'title',
200
- label: 'Title',
201
- type: 'input',
202
- placeholder: 'Widget title',
203
- },
204
- {
205
- key: 'description',
206
- label: 'Description',
207
- type: 'input',
208
- placeholder: 'Widget description',
209
- },
210
- {
211
- key: 'type',
212
- label: 'Widget type',
213
- type: 'select',
214
- options: WIDGET_TYPE_OPTIONS,
215
- defaultValue: 'metric',
216
- },
217
- ],
218
- },
219
-
220
- // ----- Data Binding (chart / metric / table / list / custom) ---------
221
- {
222
- key: 'data',
223
- title: 'Data Binding',
224
- collapsible: true,
225
- visibleWhen: (d: Record<string, any>) => d.type !== 'pivot',
226
- fields: [
227
- objectField,
228
- categoryFieldDef,
229
- valueFieldDef,
230
- {
231
- key: 'aggregate',
232
- label: 'Aggregation',
233
- type: 'select',
234
- options: AGGREGATE_OPTIONS,
235
- defaultValue: 'count',
236
- },
237
- ],
238
- },
239
-
240
- // ----- Pivot: Data source -------------------------------------------
241
- {
242
- key: 'pivot-data',
243
- title: 'Data',
244
- collapsible: true,
245
- visibleWhen: (d: Record<string, any>) => d.type === 'pivot',
246
- fields: [
247
- objectField,
248
- ],
249
- },
250
-
251
- // ----- Pivot: Rows --------------------------------------------------
252
- {
253
- key: 'pivot-rows',
254
- title: 'Rows',
255
- collapsible: true,
256
- visibleWhen: (d: Record<string, any>) => d.type === 'pivot',
257
- fields: [
258
- pivotRowField,
259
- {
260
- key: 'rowSortBy',
261
- label: 'Sort by',
262
- type: 'icon-group',
263
- options: SORT_BY_OPTIONS,
264
- defaultValue: 'group',
265
- },
266
- {
267
- key: 'rowSortOrder',
268
- label: 'Sort order',
269
- type: 'icon-group',
270
- options: SORT_ORDER_OPTIONS,
271
- defaultValue: 'asc',
272
- },
273
- {
274
- key: 'showRowLabels',
275
- label: 'Show label',
276
- type: 'switch',
277
- defaultValue: true,
278
- },
279
- {
280
- key: 'showRowTotals',
281
- label: 'Show totals',
282
- type: 'switch',
283
- defaultValue: false,
284
- },
285
- ],
286
- },
287
-
288
- // ----- Pivot: Columns -----------------------------------------------
289
- {
290
- key: 'pivot-columns',
291
- title: 'Columns',
292
- collapsible: true,
293
- visibleWhen: (d: Record<string, any>) => d.type === 'pivot',
294
- fields: [
295
- pivotColumnField,
296
- {
297
- key: 'columnSortBy',
298
- label: 'Sort by',
299
- type: 'icon-group',
300
- options: SORT_BY_OPTIONS,
301
- defaultValue: 'group',
302
- },
303
- {
304
- key: 'columnSortOrder',
305
- label: 'Sort order',
306
- type: 'icon-group',
307
- options: SORT_ORDER_OPTIONS,
308
- defaultValue: 'asc',
309
- },
310
- {
311
- key: 'showColumnLabels',
312
- label: 'Show label',
313
- type: 'switch',
314
- defaultValue: true,
315
- },
316
- {
317
- key: 'showColumnTotals',
318
- label: 'Show totals',
319
- type: 'switch',
320
- defaultValue: false,
321
- },
322
- ],
323
- },
324
-
325
- // ----- Pivot: Values ------------------------------------------------
326
- {
327
- key: 'pivot-values',
328
- title: 'Values',
329
- collapsible: true,
330
- visibleWhen: (d: Record<string, any>) => d.type === 'pivot',
331
- fields: [
332
- pivotValueField,
333
- {
334
- key: 'aggregation',
335
- label: 'Aggregation',
336
- type: 'select',
337
- options: AGGREGATE_OPTIONS,
338
- defaultValue: 'sum',
339
- },
340
- {
341
- key: 'format',
342
- label: 'Number format',
343
- type: 'input',
344
- placeholder: 'e.g. $,.2f',
345
- },
346
- ],
347
- },
348
-
349
- // ----- Chart: Axis & Series (visible for chart types) ---------------
350
- {
351
- key: 'chart-axis',
352
- title: 'Axis & Series',
353
- collapsible: true,
354
- visibleWhen: (d: Record<string, any>) => isChartType(d.type),
355
- fields: [
356
- {
357
- key: 'xAxisLabel',
358
- label: 'X-axis label',
359
- type: 'input',
360
- placeholder: 'e.g. Month',
361
- },
362
- {
363
- key: 'yAxisLabel',
364
- label: 'Y-axis label',
365
- type: 'input',
366
- placeholder: 'e.g. Revenue',
367
- },
368
- {
369
- key: 'showLegend',
370
- label: 'Show legend',
371
- type: 'switch',
372
- defaultValue: true,
373
- },
374
- ],
375
- },
376
-
377
- // ----- Table: Column config (visible for table type) ----------------
378
- {
379
- key: 'table-columns',
380
- title: 'Columns',
381
- collapsible: true,
382
- visibleWhen: (d: Record<string, any>) => d.type === 'table',
383
- fields: [
384
- {
385
- key: 'searchable',
386
- label: 'Searchable',
387
- type: 'switch',
388
- defaultValue: false,
389
- },
390
- {
391
- key: 'pagination',
392
- label: 'Pagination',
393
- type: 'switch',
394
- defaultValue: false,
395
- },
396
- ],
397
- },
398
-
399
- // ----- Layout (always visible) --------------------------------------
400
- {
401
- key: 'layout',
402
- title: 'Layout',
403
- collapsible: true,
404
- fields: [
405
- {
406
- key: 'layoutW',
407
- label: 'Width (columns)',
408
- type: 'slider',
409
- min: 1,
410
- max: 12,
411
- step: 1,
412
- defaultValue: 1,
413
- },
414
- {
415
- key: 'layoutH',
416
- label: 'Height (rows)',
417
- type: 'slider',
418
- min: 1,
419
- max: 6,
420
- step: 1,
421
- defaultValue: 1,
422
- },
423
- ],
424
- },
425
-
426
- // ----- Appearance (always visible, collapsed by default) ------------
427
- {
428
- key: 'appearance',
429
- title: 'Appearance',
430
- collapsible: true,
431
- defaultCollapsed: true,
432
- fields: [
433
- {
434
- key: 'colorVariant',
435
- label: 'Color variant',
436
- type: 'select',
437
- options: COLOR_VARIANT_OPTIONS,
438
- defaultValue: 'default',
439
- },
440
- {
441
- key: 'actionUrl',
442
- label: 'Action URL',
443
- type: 'input',
444
- placeholder: 'https://...',
445
- },
446
- ],
447
- },
448
- ],
449
- };
450
- }
451
-
452
- // ---------------------------------------------------------------------------
453
- // Props
454
- // ---------------------------------------------------------------------------
455
-
456
- export interface WidgetConfigPanelProps {
457
- /** Whether the panel is open */
458
- open: boolean;
459
- /** Close handler */
460
- onClose: () => void;
461
- /** Widget configuration (flattened: layout.w → layoutW, layout.h → layoutH) */
462
- config: Record<string, any>;
463
- /** Persist the updated widget config */
464
- onSave: (config: Record<string, any>) => void;
465
- /** Optional live-update callback */
466
- onFieldChange?: (field: string, value: any) => void;
467
- /** Extra content rendered in the header row (e.g. delete button) */
468
- headerExtra?: React.ReactNode;
469
- /** Available data-source objects for dropdown selection */
470
- availableObjects?: SelectOption[];
471
- /** Available fields of the currently selected object for dropdown selection */
472
- availableFields?: SelectOption[];
473
- }
474
-
475
- // ---------------------------------------------------------------------------
476
- // Component
477
- // ---------------------------------------------------------------------------
478
-
479
- /** Resolve an I18nLabel (string or {key, defaultValue}) to a plain string. */
480
- function resolveLabel(v: unknown): string {
481
- if (v === undefined || v === null) return '';
482
- if (typeof v === 'string') return v;
483
- if (typeof v === 'object') {
484
- const obj = v as Record<string, any>;
485
- return obj.defaultValue || obj.key || '';
486
- }
487
- return String(v);
488
- }
489
-
490
- /**
491
- * WidgetConfigPanel — Schema-driven configuration panel for individual
492
- * dashboard widgets.
493
- *
494
- * Supports editing: title, description, type, data binding (object,
495
- * categoryField, valueField, aggregate), layout (width, height),
496
- * appearance (colorVariant, actionUrl).
497
- *
498
- * Sections are context-aware: pivot, table, and chart types each show
499
- * their own dedicated config sections via `visibleWhen` predicates.
500
- */
501
- export function WidgetConfigPanel({
502
- open,
503
- onClose,
504
- config,
505
- onSave,
506
- onFieldChange,
507
- headerExtra,
508
- availableObjects,
509
- availableFields,
510
- }: WidgetConfigPanelProps) {
511
- // Pre-process config to resolve any I18nLabel values for title/description
512
- const normalizedConfig: Record<string, any> = React.useMemo(() => ({
513
- ...config,
514
- title: typeof config.title === 'object' ? resolveLabel(config.title) : config.title,
515
- description: typeof config.description === 'object' ? resolveLabel(config.description) : config.description,
516
- }), [config]);
517
-
518
- const { draft, isDirty, updateField, discard } = useConfigDraft(normalizedConfig, {
519
- onUpdate: onFieldChange,
520
- });
521
-
522
- const schema = React.useMemo(
523
- () => buildWidgetSchema(availableObjects, availableFields, draft.type),
524
- [availableObjects, availableFields, draft.type],
525
- );
526
-
527
- return (
528
- <ConfigPanelRenderer
529
- open={open}
530
- onClose={onClose}
531
- schema={schema}
532
- draft={draft}
533
- isDirty={isDirty}
534
- onFieldChange={updateField}
535
- onSave={() => onSave(draft)}
536
- onDiscard={discard}
537
- headerExtra={headerExtra}
538
- />
539
- );
540
- }