@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.
- package/.turbo/turbo-build.log +40 -7
- package/dist/index.js +3848 -2635
- package/dist/index.umd.cjs +5 -5
- package/dist/src/DashboardConfigPanel.d.ts +28 -0
- package/dist/src/DashboardConfigPanel.d.ts.map +1 -0
- package/dist/src/DashboardConfigPanel.stories.d.ts +14 -0
- package/dist/src/DashboardConfigPanel.stories.d.ts.map +1 -0
- package/dist/src/DashboardGridLayout.d.ts.map +1 -1
- package/dist/src/DashboardRenderer.d.ts +14 -0
- package/dist/src/DashboardRenderer.d.ts.map +1 -1
- package/dist/src/DashboardWithConfig.d.ts +32 -0
- package/dist/src/DashboardWithConfig.d.ts.map +1 -0
- package/dist/src/MetricCard.d.ts +8 -2
- package/dist/src/MetricCard.d.ts.map +1 -1
- package/dist/src/MetricWidget.d.ts +12 -3
- package/dist/src/MetricWidget.d.ts.map +1 -1
- package/dist/src/ObjectDataTable.d.ts +39 -0
- package/dist/src/ObjectDataTable.d.ts.map +1 -0
- package/dist/src/ObjectPivotTable.d.ts +29 -0
- package/dist/src/ObjectPivotTable.d.ts.map +1 -0
- package/dist/src/PivotTable.d.ts +14 -0
- package/dist/src/PivotTable.d.ts.map +1 -0
- package/dist/src/WidgetConfigPanel.d.ts +43 -0
- package/dist/src/WidgetConfigPanel.d.ts.map +1 -0
- package/dist/src/index.d.ts +13 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/utils.d.ts +14 -0
- package/dist/src/utils.d.ts.map +1 -0
- package/package.json +7 -7
- package/src/DashboardConfigPanel.stories.tsx +164 -0
- package/src/DashboardConfigPanel.tsx +158 -0
- package/src/DashboardGridLayout.tsx +101 -3
- package/src/DashboardRenderer.tsx +269 -28
- package/src/DashboardWithConfig.tsx +211 -0
- package/src/MetricCard.tsx +11 -4
- package/src/MetricWidget.tsx +18 -11
- package/src/ObjectDataTable.tsx +191 -0
- package/src/ObjectPivotTable.tsx +160 -0
- package/src/PivotTable.tsx +262 -0
- package/src/WidgetConfigPanel.tsx +540 -0
- package/src/__tests__/DashboardConfigPanel.test.tsx +206 -0
- package/src/__tests__/DashboardRenderer.designMode.test.tsx +386 -0
- package/src/__tests__/DashboardRenderer.header.test.tsx +114 -0
- package/src/__tests__/DashboardRenderer.mobile.test.tsx +214 -0
- package/src/__tests__/DashboardRenderer.widgetData.test.tsx +1022 -0
- package/src/__tests__/DashboardWithConfig.test.tsx +276 -0
- package/src/__tests__/MetricCard.test.tsx +23 -0
- package/src/__tests__/ObjectDataTable.test.tsx +122 -0
- package/src/__tests__/ObjectPivotTable.test.tsx +192 -0
- package/src/__tests__/PivotTable.test.tsx +162 -0
- package/src/__tests__/WidgetConfigPanel.test.tsx +492 -0
- package/src/__tests__/ensureWidgetIds.test.tsx +103 -0
- package/src/index.tsx +107 -1
- 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
|
+
}
|