@object-ui/plugin-dashboard 3.0.3 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/.turbo/turbo-build.log +40 -7
  2. package/dist/index.js +3848 -2635
  3. package/dist/index.umd.cjs +5 -5
  4. package/dist/src/DashboardConfigPanel.d.ts +28 -0
  5. package/dist/src/DashboardConfigPanel.d.ts.map +1 -0
  6. package/dist/src/DashboardConfigPanel.stories.d.ts +14 -0
  7. package/dist/src/DashboardConfigPanel.stories.d.ts.map +1 -0
  8. package/dist/src/DashboardGridLayout.d.ts.map +1 -1
  9. package/dist/src/DashboardRenderer.d.ts +14 -0
  10. package/dist/src/DashboardRenderer.d.ts.map +1 -1
  11. package/dist/src/DashboardWithConfig.d.ts +32 -0
  12. package/dist/src/DashboardWithConfig.d.ts.map +1 -0
  13. package/dist/src/MetricCard.d.ts +8 -2
  14. package/dist/src/MetricCard.d.ts.map +1 -1
  15. package/dist/src/MetricWidget.d.ts +12 -3
  16. package/dist/src/MetricWidget.d.ts.map +1 -1
  17. package/dist/src/ObjectDataTable.d.ts +39 -0
  18. package/dist/src/ObjectDataTable.d.ts.map +1 -0
  19. package/dist/src/ObjectPivotTable.d.ts +29 -0
  20. package/dist/src/ObjectPivotTable.d.ts.map +1 -0
  21. package/dist/src/PivotTable.d.ts +14 -0
  22. package/dist/src/PivotTable.d.ts.map +1 -0
  23. package/dist/src/WidgetConfigPanel.d.ts +43 -0
  24. package/dist/src/WidgetConfigPanel.d.ts.map +1 -0
  25. package/dist/src/index.d.ts +13 -1
  26. package/dist/src/index.d.ts.map +1 -1
  27. package/dist/src/utils.d.ts +14 -0
  28. package/dist/src/utils.d.ts.map +1 -0
  29. package/package.json +7 -7
  30. package/src/DashboardConfigPanel.stories.tsx +164 -0
  31. package/src/DashboardConfigPanel.tsx +158 -0
  32. package/src/DashboardGridLayout.tsx +101 -3
  33. package/src/DashboardRenderer.tsx +269 -28
  34. package/src/DashboardWithConfig.tsx +211 -0
  35. package/src/MetricCard.tsx +11 -4
  36. package/src/MetricWidget.tsx +18 -11
  37. package/src/ObjectDataTable.tsx +191 -0
  38. package/src/ObjectPivotTable.tsx +160 -0
  39. package/src/PivotTable.tsx +262 -0
  40. package/src/WidgetConfigPanel.tsx +540 -0
  41. package/src/__tests__/DashboardConfigPanel.test.tsx +206 -0
  42. package/src/__tests__/DashboardRenderer.designMode.test.tsx +386 -0
  43. package/src/__tests__/DashboardRenderer.header.test.tsx +114 -0
  44. package/src/__tests__/DashboardRenderer.mobile.test.tsx +214 -0
  45. package/src/__tests__/DashboardRenderer.widgetData.test.tsx +1022 -0
  46. package/src/__tests__/DashboardWithConfig.test.tsx +276 -0
  47. package/src/__tests__/MetricCard.test.tsx +23 -0
  48. package/src/__tests__/ObjectDataTable.test.tsx +122 -0
  49. package/src/__tests__/ObjectPivotTable.test.tsx +192 -0
  50. package/src/__tests__/PivotTable.test.tsx +162 -0
  51. package/src/__tests__/WidgetConfigPanel.test.tsx +492 -0
  52. package/src/__tests__/ensureWidgetIds.test.tsx +103 -0
  53. package/src/index.tsx +107 -1
  54. package/src/utils.ts +17 -0
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Tests for the ensureWidgetIds helper pattern.
3
+ * Validates that widgets without IDs get auto-assigned unique IDs.
4
+ */
5
+
6
+ import { describe, it, expect } from 'vitest';
7
+ import type { DashboardSchema } from '@object-ui/types';
8
+
9
+ /**
10
+ * Portable implementation of ensureWidgetIds — mirrors
11
+ * apps/console/src/components/DashboardView.tsx.
12
+ */
13
+ let counter = 0;
14
+ function createWidgetId(): string {
15
+ counter += 1;
16
+ return `widget_test_${counter}`;
17
+ }
18
+
19
+ function ensureWidgetIds(schema: DashboardSchema): DashboardSchema {
20
+ if (!schema.widgets?.length) return schema;
21
+ const needsFix = schema.widgets.some((w) => !w.id);
22
+ if (!needsFix) return schema;
23
+ return {
24
+ ...schema,
25
+ widgets: schema.widgets.map((w) => (w.id ? w : { ...w, id: createWidgetId() })),
26
+ };
27
+ }
28
+
29
+ describe('ensureWidgetIds', () => {
30
+ beforeEach(() => {
31
+ counter = 0;
32
+ });
33
+ it('should return same schema when all widgets have IDs', () => {
34
+ const schema: DashboardSchema = {
35
+ type: 'dashboard',
36
+ columns: 3,
37
+ widgets: [
38
+ { id: 'w1', title: 'A', type: 'metric' },
39
+ { id: 'w2', title: 'B', type: 'bar' },
40
+ ],
41
+ };
42
+ const result = ensureWidgetIds(schema);
43
+ expect(result).toBe(schema); // same reference — no mutation
44
+ });
45
+
46
+ it('should assign IDs to widgets missing them', () => {
47
+ const schema: DashboardSchema = {
48
+ type: 'dashboard',
49
+ columns: 3,
50
+ widgets: [
51
+ { title: 'No ID', type: 'metric' },
52
+ { id: 'w2', title: 'Has ID', type: 'bar' },
53
+ ],
54
+ };
55
+ const result = ensureWidgetIds(schema);
56
+ expect(result).not.toBe(schema);
57
+ expect(result.widgets[0].id).toBeTruthy();
58
+ expect(result.widgets[1].id).toBe('w2');
59
+ });
60
+
61
+ it('should handle empty widgets array', () => {
62
+ const schema: DashboardSchema = {
63
+ type: 'dashboard',
64
+ columns: 3,
65
+ widgets: [],
66
+ };
67
+ const result = ensureWidgetIds(schema);
68
+ expect(result).toBe(schema);
69
+ });
70
+
71
+ it('should generate unique IDs for multiple missing widgets', () => {
72
+ const schema: DashboardSchema = {
73
+ type: 'dashboard',
74
+ columns: 3,
75
+ widgets: [
76
+ { title: 'A', type: 'metric' },
77
+ { title: 'B', type: 'bar' },
78
+ { title: 'C', type: 'pie' },
79
+ ],
80
+ };
81
+ const result = ensureWidgetIds(schema);
82
+ const ids = result.widgets.map((w) => w.id);
83
+ // All IDs should be unique
84
+ expect(new Set(ids).size).toBe(ids.length);
85
+ // All IDs should be truthy
86
+ ids.forEach((id) => expect(id).toBeTruthy());
87
+ });
88
+
89
+ it('should preserve existing widget data', () => {
90
+ const schema: DashboardSchema = {
91
+ type: 'dashboard',
92
+ columns: 3,
93
+ widgets: [
94
+ { title: 'Revenue', type: 'metric', object: 'orders', layout: { x: 0, y: 0, w: 2, h: 1 } },
95
+ ],
96
+ };
97
+ const result = ensureWidgetIds(schema);
98
+ expect(result.widgets[0].title).toBe('Revenue');
99
+ expect(result.widgets[0].type).toBe('metric');
100
+ expect(result.widgets[0].object).toBe('orders');
101
+ expect(result.widgets[0].layout).toEqual({ x: 0, y: 0, w: 2, h: 1 });
102
+ });
103
+ });
package/src/index.tsx CHANGED
@@ -11,8 +11,14 @@ import { DashboardRenderer } from './DashboardRenderer';
11
11
  import { DashboardGridLayout } from './DashboardGridLayout';
12
12
  import { MetricWidget } from './MetricWidget';
13
13
  import { MetricCard } from './MetricCard';
14
+ import { PivotTable } from './PivotTable';
15
+ import { ObjectPivotTable } from './ObjectPivotTable';
16
+ import { ObjectDataTable } from './ObjectDataTable';
17
+ import { DashboardConfigPanel } from './DashboardConfigPanel';
18
+ import { WidgetConfigPanel } from './WidgetConfigPanel';
19
+ import { DashboardWithConfig } from './DashboardWithConfig';
14
20
 
15
- export { DashboardRenderer, DashboardGridLayout, MetricWidget, MetricCard };
21
+ export { DashboardRenderer, DashboardGridLayout, MetricWidget, MetricCard, PivotTable, ObjectPivotTable, ObjectDataTable, DashboardConfigPanel, WidgetConfigPanel, DashboardWithConfig };
16
22
 
17
23
  // Register dashboard component
18
24
  ComponentRegistry.register(
@@ -77,6 +83,77 @@ ComponentRegistry.register(
77
83
  }
78
84
  );
79
85
 
86
+ // Register pivot table component
87
+ ComponentRegistry.register(
88
+ 'pivot',
89
+ PivotTable,
90
+ {
91
+ namespace: 'plugin-dashboard',
92
+ label: 'Pivot Table',
93
+ category: 'Dashboard',
94
+ icon: 'table-2',
95
+ inputs: [
96
+ { name: 'title', type: 'string', label: 'Title' },
97
+ { name: 'rowField', type: 'string', label: 'Row Field', required: true },
98
+ { name: 'columnField', type: 'string', label: 'Column Field', required: true },
99
+ { name: 'valueField', type: 'string', label: 'Value Field', required: true },
100
+ { name: 'aggregation', type: 'enum', label: 'Aggregation', enum: [
101
+ { label: 'Sum', value: 'sum' },
102
+ { label: 'Count', value: 'count' },
103
+ { label: 'Average', value: 'avg' },
104
+ { label: 'Min', value: 'min' },
105
+ { label: 'Max', value: 'max' },
106
+ ]},
107
+ { name: 'showRowTotals', type: 'boolean', label: 'Show Row Totals' },
108
+ { name: 'showColumnTotals', type: 'boolean', label: 'Show Column Totals' },
109
+ { name: 'format', type: 'string', label: 'Number Format' },
110
+ ],
111
+ defaultProps: {
112
+ rowField: '',
113
+ columnField: '',
114
+ valueField: '',
115
+ aggregation: 'sum',
116
+ data: [],
117
+ }
118
+ }
119
+ );
120
+
121
+ // Register object-aware pivot table (async data loading)
122
+ ComponentRegistry.register(
123
+ 'object-pivot',
124
+ ObjectPivotTable,
125
+ {
126
+ namespace: 'plugin-dashboard',
127
+ label: 'Object Pivot Table',
128
+ category: 'Dashboard',
129
+ icon: 'table-2',
130
+ inputs: [
131
+ { name: 'objectName', type: 'string', label: 'Object Name', required: true },
132
+ { name: 'title', type: 'string', label: 'Title' },
133
+ { name: 'rowField', type: 'string', label: 'Row Field', required: true },
134
+ { name: 'columnField', type: 'string', label: 'Column Field', required: true },
135
+ { name: 'valueField', type: 'string', label: 'Value Field', required: true },
136
+ { name: 'aggregation', type: 'enum', label: 'Aggregation', enum: [
137
+ { label: 'Sum', value: 'sum' },
138
+ { label: 'Count', value: 'count' },
139
+ { label: 'Average', value: 'avg' },
140
+ { label: 'Min', value: 'min' },
141
+ { label: 'Max', value: 'max' },
142
+ ]},
143
+ { name: 'showRowTotals', type: 'boolean', label: 'Show Row Totals' },
144
+ { name: 'showColumnTotals', type: 'boolean', label: 'Show Column Totals' },
145
+ { name: 'filter', type: 'array', label: 'Filter' },
146
+ { name: 'format', type: 'string', label: 'Number Format' },
147
+ ],
148
+ defaultProps: {
149
+ rowField: '',
150
+ columnField: '',
151
+ valueField: '',
152
+ aggregation: 'sum',
153
+ }
154
+ }
155
+ );
156
+
80
157
  // Register dashboard grid layout component
81
158
  ComponentRegistry.register(
82
159
  'dashboard-grid',
@@ -99,10 +176,39 @@ ComponentRegistry.register(
99
176
  }
100
177
  );
101
178
 
179
+ // Register object-aware data table (async data loading)
180
+ ComponentRegistry.register(
181
+ 'object-data-table',
182
+ ObjectDataTable,
183
+ {
184
+ namespace: 'plugin-dashboard',
185
+ label: 'Object Data Table',
186
+ category: 'Dashboard',
187
+ icon: 'table',
188
+ inputs: [
189
+ { name: 'objectName', type: 'string', label: 'Object Name', required: true },
190
+ { name: 'columns', type: 'array', label: 'Columns' },
191
+ { name: 'filter', type: 'array', label: 'Filter' },
192
+ { name: 'searchable', type: 'boolean', label: 'Searchable' },
193
+ { name: 'pagination', type: 'boolean', label: 'Pagination' },
194
+ ],
195
+ defaultProps: {
196
+ searchable: false,
197
+ pagination: false,
198
+ }
199
+ }
200
+ );
201
+
102
202
  // Standard Export Protocol - for manual integration
103
203
  export const dashboardComponents = {
104
204
  DashboardRenderer,
105
205
  DashboardGridLayout,
106
206
  MetricWidget,
107
207
  MetricCard,
208
+ PivotTable,
209
+ ObjectPivotTable,
210
+ ObjectDataTable,
211
+ DashboardConfigPanel,
212
+ WidgetConfigPanel,
213
+ DashboardWithConfig,
108
214
  };
package/src/utils.ts ADDED
@@ -0,0 +1,17 @@
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
+ /** Returns true when the widget data config uses provider: 'object' (async data source). */
10
+ export function isObjectProvider(widgetData: unknown): widgetData is { provider: 'object'; object?: string; aggregate?: any } {
11
+ return (
12
+ widgetData != null &&
13
+ typeof widgetData === 'object' &&
14
+ !Array.isArray(widgetData) &&
15
+ (widgetData as any).provider === 'object'
16
+ );
17
+ }