@object-ui/plugin-dashboard 3.3.0 → 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.
- package/CHANGELOG.md +10 -0
- package/README.md +21 -1
- package/dist/index.js +869 -787
- package/dist/index.umd.cjs +4 -4
- package/dist/packages/plugin-dashboard/src/DashboardRenderer.d.ts +5 -0
- package/dist/packages/plugin-dashboard/src/DashboardRenderer.d.ts.map +1 -1
- package/dist/packages/plugin-dashboard/src/MetricWidget.d.ts +4 -1
- package/dist/packages/plugin-dashboard/src/MetricWidget.d.ts.map +1 -1
- package/dist/packages/plugin-dashboard/src/ObjectMetricWidget.d.ts +2 -0
- package/dist/packages/plugin-dashboard/src/ObjectMetricWidget.d.ts.map +1 -1
- package/dist/packages/plugin-dashboard/src/index.d.ts +1 -1
- package/package.json +40 -7
- package/.turbo/turbo-build.log +0 -41
- package/src/DashboardConfigPanel.stories.tsx +0 -164
- package/src/DashboardConfigPanel.tsx +0 -158
- package/src/DashboardGridLayout.tsx +0 -367
- package/src/DashboardRenderer.stories.tsx +0 -173
- package/src/DashboardRenderer.tsx +0 -479
- package/src/DashboardWithConfig.tsx +0 -211
- package/src/MetricCard.tsx +0 -102
- package/src/MetricWidget.tsx +0 -96
- package/src/ObjectDataTable.tsx +0 -226
- package/src/ObjectMetricWidget.tsx +0 -159
- package/src/ObjectPivotTable.tsx +0 -160
- package/src/PivotTable.tsx +0 -262
- package/src/WidgetConfigPanel.tsx +0 -540
- package/src/__tests__/DashboardConfigPanel.test.tsx +0 -206
- package/src/__tests__/DashboardGridLayout.test.tsx +0 -199
- package/src/__tests__/DashboardRenderer.autoRefresh.test.tsx +0 -124
- package/src/__tests__/DashboardRenderer.designMode.test.tsx +0 -386
- package/src/__tests__/DashboardRenderer.header.test.tsx +0 -114
- package/src/__tests__/DashboardRenderer.mobile.test.tsx +0 -214
- package/src/__tests__/DashboardRenderer.widgetData.test.tsx +0 -1411
- package/src/__tests__/DashboardWithConfig.test.tsx +0 -276
- package/src/__tests__/MetricCard.test.tsx +0 -107
- package/src/__tests__/ObjectDataTable.test.tsx +0 -211
- package/src/__tests__/ObjectMetricWidget.test.tsx +0 -196
- package/src/__tests__/ObjectPivotTable.test.tsx +0 -192
- package/src/__tests__/PivotTable.test.tsx +0 -162
- package/src/__tests__/WidgetConfigPanel.test.tsx +0 -492
- package/src/__tests__/ensureWidgetIds.test.tsx +0 -103
- package/src/index.tsx +0 -236
- package/src/utils.ts +0 -17
- package/tsconfig.json +0 -19
- package/vite.config.ts +0 -64
- package/vitest.config.ts +0 -9
- package/vitest.setup.tsx +0 -18
|
@@ -1,386 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
-
import { render, screen, fireEvent } from '@testing-library/react';
|
|
3
|
-
import { DashboardRenderer } from '../DashboardRenderer';
|
|
4
|
-
import type { DashboardSchema } from '@object-ui/types';
|
|
5
|
-
|
|
6
|
-
// Mock SchemaRenderer to avoid pulling in the full renderer tree.
|
|
7
|
-
// Forwards className and includes an interactive child to simulate real chart content.
|
|
8
|
-
vi.mock('@object-ui/react', () => ({
|
|
9
|
-
SchemaRenderer: ({ schema, className }: { schema: any; className?: string }) => (
|
|
10
|
-
<div data-testid="schema-renderer" className={className}>
|
|
11
|
-
<button data-testid={`interactive-child-${schema?.type ?? 'unknown'}`}>
|
|
12
|
-
{schema?.type ?? 'unknown'}
|
|
13
|
-
</button>
|
|
14
|
-
</div>
|
|
15
|
-
),
|
|
16
|
-
}));
|
|
17
|
-
|
|
18
|
-
const DASHBOARD_WITH_WIDGETS: DashboardSchema = {
|
|
19
|
-
type: 'dashboard',
|
|
20
|
-
title: 'Test Dashboard',
|
|
21
|
-
columns: 2,
|
|
22
|
-
widgets: [
|
|
23
|
-
{ id: 'w1', title: 'Revenue', type: 'metric' },
|
|
24
|
-
{ id: 'w2', title: 'Sales Chart', type: 'bar' },
|
|
25
|
-
{ id: 'w3', title: 'Orders Table', type: 'table' },
|
|
26
|
-
],
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
describe('DashboardRenderer design mode', () => {
|
|
30
|
-
describe('Widget selection', () => {
|
|
31
|
-
it('should render widget test IDs in design mode', () => {
|
|
32
|
-
const onWidgetClick = vi.fn();
|
|
33
|
-
render(
|
|
34
|
-
<DashboardRenderer
|
|
35
|
-
schema={DASHBOARD_WITH_WIDGETS}
|
|
36
|
-
designMode
|
|
37
|
-
selectedWidgetId={null}
|
|
38
|
-
onWidgetClick={onWidgetClick}
|
|
39
|
-
/>,
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
expect(screen.getByTestId('dashboard-preview-widget-w1')).toBeInTheDocument();
|
|
43
|
-
expect(screen.getByTestId('dashboard-preview-widget-w2')).toBeInTheDocument();
|
|
44
|
-
expect(screen.getByTestId('dashboard-preview-widget-w3')).toBeInTheDocument();
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('should not render widget test IDs when not in design mode', () => {
|
|
48
|
-
render(<DashboardRenderer schema={DASHBOARD_WITH_WIDGETS} />);
|
|
49
|
-
|
|
50
|
-
expect(screen.queryByTestId('dashboard-preview-widget-w1')).not.toBeInTheDocument();
|
|
51
|
-
expect(screen.queryByTestId('dashboard-preview-widget-w2')).not.toBeInTheDocument();
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('should call onWidgetClick when a widget is clicked', () => {
|
|
55
|
-
const onWidgetClick = vi.fn();
|
|
56
|
-
render(
|
|
57
|
-
<DashboardRenderer
|
|
58
|
-
schema={DASHBOARD_WITH_WIDGETS}
|
|
59
|
-
designMode
|
|
60
|
-
selectedWidgetId={null}
|
|
61
|
-
onWidgetClick={onWidgetClick}
|
|
62
|
-
/>,
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
fireEvent.click(screen.getByTestId('dashboard-preview-widget-w1'));
|
|
66
|
-
expect(onWidgetClick).toHaveBeenCalledWith('w1');
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('should set aria-selected on the selected widget', () => {
|
|
70
|
-
render(
|
|
71
|
-
<DashboardRenderer
|
|
72
|
-
schema={DASHBOARD_WITH_WIDGETS}
|
|
73
|
-
designMode
|
|
74
|
-
selectedWidgetId="w2"
|
|
75
|
-
onWidgetClick={vi.fn()}
|
|
76
|
-
/>,
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
expect(screen.getByTestId('dashboard-preview-widget-w2')).toHaveAttribute('aria-selected', 'true');
|
|
80
|
-
expect(screen.getByTestId('dashboard-preview-widget-w1')).toHaveAttribute('aria-selected', 'false');
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('should apply selection styling classes on selected widget', () => {
|
|
84
|
-
const { container } = render(
|
|
85
|
-
<DashboardRenderer
|
|
86
|
-
schema={DASHBOARD_WITH_WIDGETS}
|
|
87
|
-
designMode
|
|
88
|
-
selectedWidgetId="w1"
|
|
89
|
-
onWidgetClick={vi.fn()}
|
|
90
|
-
/>,
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
const selectedWidget = screen.getByTestId('dashboard-preview-widget-w1');
|
|
94
|
-
expect(selectedWidget.className).toContain('ring-2');
|
|
95
|
-
expect(selectedWidget.className).toContain('ring-primary');
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it('should have role="button" on widgets in design mode', () => {
|
|
99
|
-
render(
|
|
100
|
-
<DashboardRenderer
|
|
101
|
-
schema={DASHBOARD_WITH_WIDGETS}
|
|
102
|
-
designMode
|
|
103
|
-
selectedWidgetId={null}
|
|
104
|
-
onWidgetClick={vi.fn()}
|
|
105
|
-
/>,
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
expect(screen.getByTestId('dashboard-preview-widget-w1')).toHaveAttribute('role', 'button');
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it('should have tabIndex=0 for keyboard accessibility in design mode', () => {
|
|
112
|
-
render(
|
|
113
|
-
<DashboardRenderer
|
|
114
|
-
schema={DASHBOARD_WITH_WIDGETS}
|
|
115
|
-
designMode
|
|
116
|
-
selectedWidgetId={null}
|
|
117
|
-
onWidgetClick={vi.fn()}
|
|
118
|
-
/>,
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
expect(screen.getByTestId('dashboard-preview-widget-w1')).toHaveAttribute('tabindex', '0');
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it('should have aria-label on widgets in design mode', () => {
|
|
125
|
-
render(
|
|
126
|
-
<DashboardRenderer
|
|
127
|
-
schema={DASHBOARD_WITH_WIDGETS}
|
|
128
|
-
designMode
|
|
129
|
-
selectedWidgetId={null}
|
|
130
|
-
onWidgetClick={vi.fn()}
|
|
131
|
-
/>,
|
|
132
|
-
);
|
|
133
|
-
|
|
134
|
-
expect(screen.getByTestId('dashboard-preview-widget-w1')).toHaveAttribute(
|
|
135
|
-
'aria-label',
|
|
136
|
-
'Widget: Revenue',
|
|
137
|
-
);
|
|
138
|
-
});
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
describe('Click deselection', () => {
|
|
142
|
-
it('should call onWidgetClick(null) when clicking background', () => {
|
|
143
|
-
const onWidgetClick = vi.fn();
|
|
144
|
-
const { container } = render(
|
|
145
|
-
<DashboardRenderer
|
|
146
|
-
schema={DASHBOARD_WITH_WIDGETS}
|
|
147
|
-
designMode
|
|
148
|
-
selectedWidgetId="w1"
|
|
149
|
-
onWidgetClick={onWidgetClick}
|
|
150
|
-
/>,
|
|
151
|
-
);
|
|
152
|
-
|
|
153
|
-
// Click on the outer grid container (background)
|
|
154
|
-
const gridContainer = container.firstElementChild as HTMLElement;
|
|
155
|
-
fireEvent.click(gridContainer);
|
|
156
|
-
expect(onWidgetClick).toHaveBeenCalledWith(null);
|
|
157
|
-
});
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
describe('Keyboard navigation', () => {
|
|
161
|
-
it('should select next widget with ArrowRight', () => {
|
|
162
|
-
const onWidgetClick = vi.fn();
|
|
163
|
-
render(
|
|
164
|
-
<DashboardRenderer
|
|
165
|
-
schema={DASHBOARD_WITH_WIDGETS}
|
|
166
|
-
designMode
|
|
167
|
-
selectedWidgetId="w1"
|
|
168
|
-
onWidgetClick={onWidgetClick}
|
|
169
|
-
/>,
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
fireEvent.keyDown(screen.getByTestId('dashboard-preview-widget-w1'), { key: 'ArrowRight' });
|
|
173
|
-
expect(onWidgetClick).toHaveBeenCalledWith('w2');
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
it('should select previous widget with ArrowLeft', () => {
|
|
177
|
-
const onWidgetClick = vi.fn();
|
|
178
|
-
render(
|
|
179
|
-
<DashboardRenderer
|
|
180
|
-
schema={DASHBOARD_WITH_WIDGETS}
|
|
181
|
-
designMode
|
|
182
|
-
selectedWidgetId="w2"
|
|
183
|
-
onWidgetClick={onWidgetClick}
|
|
184
|
-
/>,
|
|
185
|
-
);
|
|
186
|
-
|
|
187
|
-
fireEvent.keyDown(screen.getByTestId('dashboard-preview-widget-w2'), { key: 'ArrowLeft' });
|
|
188
|
-
expect(onWidgetClick).toHaveBeenCalledWith('w1');
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
it('should deselect with Escape', () => {
|
|
192
|
-
const onWidgetClick = vi.fn();
|
|
193
|
-
render(
|
|
194
|
-
<DashboardRenderer
|
|
195
|
-
schema={DASHBOARD_WITH_WIDGETS}
|
|
196
|
-
designMode
|
|
197
|
-
selectedWidgetId="w1"
|
|
198
|
-
onWidgetClick={onWidgetClick}
|
|
199
|
-
/>,
|
|
200
|
-
);
|
|
201
|
-
|
|
202
|
-
fireEvent.keyDown(screen.getByTestId('dashboard-preview-widget-w1'), { key: 'Escape' });
|
|
203
|
-
expect(onWidgetClick).toHaveBeenCalledWith(null);
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
it('should select with Enter key', () => {
|
|
207
|
-
const onWidgetClick = vi.fn();
|
|
208
|
-
render(
|
|
209
|
-
<DashboardRenderer
|
|
210
|
-
schema={DASHBOARD_WITH_WIDGETS}
|
|
211
|
-
designMode
|
|
212
|
-
selectedWidgetId={null}
|
|
213
|
-
onWidgetClick={onWidgetClick}
|
|
214
|
-
/>,
|
|
215
|
-
);
|
|
216
|
-
|
|
217
|
-
fireEvent.keyDown(screen.getByTestId('dashboard-preview-widget-w2'), { key: 'Enter' });
|
|
218
|
-
expect(onWidgetClick).toHaveBeenCalledWith('w2');
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
it('should select with Space key', () => {
|
|
222
|
-
const onWidgetClick = vi.fn();
|
|
223
|
-
render(
|
|
224
|
-
<DashboardRenderer
|
|
225
|
-
schema={DASHBOARD_WITH_WIDGETS}
|
|
226
|
-
designMode
|
|
227
|
-
selectedWidgetId={null}
|
|
228
|
-
onWidgetClick={onWidgetClick}
|
|
229
|
-
/>,
|
|
230
|
-
);
|
|
231
|
-
|
|
232
|
-
fireEvent.keyDown(screen.getByTestId('dashboard-preview-widget-w1'), { key: ' ' });
|
|
233
|
-
expect(onWidgetClick).toHaveBeenCalledWith('w1');
|
|
234
|
-
});
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
describe('Content pointer-events in design mode', () => {
|
|
238
|
-
it('should apply pointer-events-none to widget content in design mode', () => {
|
|
239
|
-
render(
|
|
240
|
-
<DashboardRenderer
|
|
241
|
-
schema={DASHBOARD_WITH_WIDGETS}
|
|
242
|
-
designMode
|
|
243
|
-
selectedWidgetId={null}
|
|
244
|
-
onWidgetClick={vi.fn()}
|
|
245
|
-
/>,
|
|
246
|
-
);
|
|
247
|
-
|
|
248
|
-
// Card widget (bar chart) — content wrapper should have pointer-events-none
|
|
249
|
-
const barWidget = screen.getByTestId('dashboard-preview-widget-w2');
|
|
250
|
-
const contentWrapper = barWidget.querySelector('.pointer-events-none');
|
|
251
|
-
expect(contentWrapper).toBeInTheDocument();
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
it('should apply pointer-events-none to self-contained (metric) widget content', () => {
|
|
255
|
-
render(
|
|
256
|
-
<DashboardRenderer
|
|
257
|
-
schema={DASHBOARD_WITH_WIDGETS}
|
|
258
|
-
designMode
|
|
259
|
-
selectedWidgetId={null}
|
|
260
|
-
onWidgetClick={vi.fn()}
|
|
261
|
-
/>,
|
|
262
|
-
);
|
|
263
|
-
|
|
264
|
-
// Metric widget — SchemaRenderer receives pointer-events-none className
|
|
265
|
-
const metricWidget = screen.getByTestId('dashboard-preview-widget-w1');
|
|
266
|
-
const contentWrapper = metricWidget.querySelector('.pointer-events-none');
|
|
267
|
-
expect(contentWrapper).toBeInTheDocument();
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
it('should NOT apply pointer-events-none when not in design mode', () => {
|
|
271
|
-
const { container } = render(<DashboardRenderer schema={DASHBOARD_WITH_WIDGETS} />);
|
|
272
|
-
|
|
273
|
-
// No design-mode content wrapper should have pointer-events-none class
|
|
274
|
-
// (data-table ghost rows have pointer-events-none internally, which is unrelated to design mode)
|
|
275
|
-
expect(container.querySelector('.pointer-events-none:not([data-testid="ghost-row"])')).not.toBeInTheDocument();
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
it('should still call onWidgetClick when clicking on Card-based widget content area', () => {
|
|
279
|
-
const onWidgetClick = vi.fn();
|
|
280
|
-
render(
|
|
281
|
-
<DashboardRenderer
|
|
282
|
-
schema={DASHBOARD_WITH_WIDGETS}
|
|
283
|
-
designMode
|
|
284
|
-
selectedWidgetId={null}
|
|
285
|
-
onWidgetClick={onWidgetClick}
|
|
286
|
-
/>,
|
|
287
|
-
);
|
|
288
|
-
|
|
289
|
-
// Click on the bar chart widget (Card-based)
|
|
290
|
-
fireEvent.click(screen.getByTestId('dashboard-preview-widget-w2'));
|
|
291
|
-
expect(onWidgetClick).toHaveBeenCalledWith('w2');
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
it('should still call onWidgetClick when clicking on table widget', () => {
|
|
295
|
-
const onWidgetClick = vi.fn();
|
|
296
|
-
render(
|
|
297
|
-
<DashboardRenderer
|
|
298
|
-
schema={DASHBOARD_WITH_WIDGETS}
|
|
299
|
-
designMode
|
|
300
|
-
selectedWidgetId={null}
|
|
301
|
-
onWidgetClick={onWidgetClick}
|
|
302
|
-
/>,
|
|
303
|
-
);
|
|
304
|
-
|
|
305
|
-
// Click on the table widget (Card-based)
|
|
306
|
-
fireEvent.click(screen.getByTestId('dashboard-preview-widget-w3'));
|
|
307
|
-
expect(onWidgetClick).toHaveBeenCalledWith('w3');
|
|
308
|
-
});
|
|
309
|
-
});
|
|
310
|
-
|
|
311
|
-
describe('Click-capture overlay in design mode', () => {
|
|
312
|
-
it('should render a click-capture overlay on Card-based widgets in design mode', () => {
|
|
313
|
-
render(
|
|
314
|
-
<DashboardRenderer
|
|
315
|
-
schema={DASHBOARD_WITH_WIDGETS}
|
|
316
|
-
designMode
|
|
317
|
-
selectedWidgetId={null}
|
|
318
|
-
onWidgetClick={vi.fn()}
|
|
319
|
-
/>,
|
|
320
|
-
);
|
|
321
|
-
|
|
322
|
-
// Card widget (bar chart) — should have an absolute overlay div
|
|
323
|
-
const barWidget = screen.getByTestId('dashboard-preview-widget-w2');
|
|
324
|
-
const overlay = barWidget.querySelector('[data-testid="widget-click-overlay"]');
|
|
325
|
-
expect(overlay).toBeInTheDocument();
|
|
326
|
-
expect(overlay?.className).toContain('absolute');
|
|
327
|
-
expect(overlay?.className).toContain('inset-0');
|
|
328
|
-
expect(overlay?.className).toContain('z-10');
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
it('should render a click-capture overlay on self-contained widgets in design mode', () => {
|
|
332
|
-
render(
|
|
333
|
-
<DashboardRenderer
|
|
334
|
-
schema={DASHBOARD_WITH_WIDGETS}
|
|
335
|
-
designMode
|
|
336
|
-
selectedWidgetId={null}
|
|
337
|
-
onWidgetClick={vi.fn()}
|
|
338
|
-
/>,
|
|
339
|
-
);
|
|
340
|
-
|
|
341
|
-
// Metric widget — should have an absolute overlay div
|
|
342
|
-
const metricWidget = screen.getByTestId('dashboard-preview-widget-w1');
|
|
343
|
-
const overlay = metricWidget.querySelector('[data-testid="widget-click-overlay"]');
|
|
344
|
-
expect(overlay).toBeInTheDocument();
|
|
345
|
-
expect(overlay?.className).toContain('absolute');
|
|
346
|
-
expect(overlay?.className).toContain('inset-0');
|
|
347
|
-
expect(overlay?.className).toContain('z-10');
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
it('should NOT render overlays when not in design mode', () => {
|
|
351
|
-
const { container } = render(<DashboardRenderer schema={DASHBOARD_WITH_WIDGETS} />);
|
|
352
|
-
|
|
353
|
-
expect(container.querySelector('[data-testid="widget-click-overlay"]')).not.toBeInTheDocument();
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
it('should apply relative positioning to widget container in design mode', () => {
|
|
357
|
-
render(
|
|
358
|
-
<DashboardRenderer
|
|
359
|
-
schema={DASHBOARD_WITH_WIDGETS}
|
|
360
|
-
designMode
|
|
361
|
-
selectedWidgetId={null}
|
|
362
|
-
onWidgetClick={vi.fn()}
|
|
363
|
-
/>,
|
|
364
|
-
);
|
|
365
|
-
|
|
366
|
-
// Card widget should have relative for overlay positioning
|
|
367
|
-
const barWidget = screen.getByTestId('dashboard-preview-widget-w2');
|
|
368
|
-
expect(barWidget.className).toContain('relative');
|
|
369
|
-
|
|
370
|
-
// Metric widget should have relative for overlay positioning
|
|
371
|
-
const metricWidget = screen.getByTestId('dashboard-preview-widget-w1');
|
|
372
|
-
expect(metricWidget.className).toContain('relative');
|
|
373
|
-
});
|
|
374
|
-
});
|
|
375
|
-
|
|
376
|
-
describe('Non-design mode behavior', () => {
|
|
377
|
-
it('should not add design mode attributes when designMode is off', () => {
|
|
378
|
-
const { container } = render(<DashboardRenderer schema={DASHBOARD_WITH_WIDGETS} />);
|
|
379
|
-
|
|
380
|
-
// No widget should have data-widget-id
|
|
381
|
-
expect(container.querySelector('[data-widget-id]')).not.toBeInTheDocument();
|
|
382
|
-
// No widget should have role=button
|
|
383
|
-
expect(container.querySelector('[role="button"]')).not.toBeInTheDocument();
|
|
384
|
-
});
|
|
385
|
-
});
|
|
386
|
-
});
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
-
import { render, screen } from '@testing-library/react';
|
|
3
|
-
import { DashboardRenderer } from '../DashboardRenderer';
|
|
4
|
-
import type { DashboardSchema } from '@object-ui/types';
|
|
5
|
-
|
|
6
|
-
// Mock SchemaRenderer to avoid pulling in the full renderer tree
|
|
7
|
-
vi.mock('@object-ui/react', () => ({
|
|
8
|
-
SchemaRenderer: ({ schema }: { schema: any }) => (
|
|
9
|
-
<div data-testid="schema-renderer">{schema?.type ?? 'unknown'}</div>
|
|
10
|
-
),
|
|
11
|
-
}));
|
|
12
|
-
|
|
13
|
-
describe('DashboardRenderer header', () => {
|
|
14
|
-
const baseSchema: DashboardSchema = {
|
|
15
|
-
type: 'dashboard',
|
|
16
|
-
name: 'test_dashboard',
|
|
17
|
-
title: 'Sales Dashboard',
|
|
18
|
-
description: 'Monthly overview of sales data',
|
|
19
|
-
widgets: [],
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
it('should render title when showTitle is not false', () => {
|
|
23
|
-
const schema: DashboardSchema = {
|
|
24
|
-
...baseSchema,
|
|
25
|
-
header: { showTitle: true },
|
|
26
|
-
};
|
|
27
|
-
render(<DashboardRenderer schema={schema} />);
|
|
28
|
-
|
|
29
|
-
expect(screen.getByText('Sales Dashboard')).toBeInTheDocument();
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('should not render title when showTitle is false', () => {
|
|
33
|
-
const schema: DashboardSchema = {
|
|
34
|
-
...baseSchema,
|
|
35
|
-
header: { showTitle: false },
|
|
36
|
-
};
|
|
37
|
-
render(<DashboardRenderer schema={schema} />);
|
|
38
|
-
|
|
39
|
-
expect(screen.queryByText('Sales Dashboard')).not.toBeInTheDocument();
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('should render description when showDescription is not false', () => {
|
|
43
|
-
const schema: DashboardSchema = {
|
|
44
|
-
...baseSchema,
|
|
45
|
-
header: { showDescription: true },
|
|
46
|
-
};
|
|
47
|
-
render(<DashboardRenderer schema={schema} />);
|
|
48
|
-
|
|
49
|
-
expect(screen.getByText('Monthly overview of sales data')).toBeInTheDocument();
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('should not render description when showDescription is false', () => {
|
|
53
|
-
const schema: DashboardSchema = {
|
|
54
|
-
...baseSchema,
|
|
55
|
-
header: { showDescription: false },
|
|
56
|
-
};
|
|
57
|
-
render(<DashboardRenderer schema={schema} />);
|
|
58
|
-
|
|
59
|
-
expect(screen.queryByText('Monthly overview of sales data')).not.toBeInTheDocument();
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('should render action buttons', () => {
|
|
63
|
-
const schema: DashboardSchema = {
|
|
64
|
-
...baseSchema,
|
|
65
|
-
header: {
|
|
66
|
-
actions: [
|
|
67
|
-
{ label: 'Export', action: 'export' },
|
|
68
|
-
{ label: 'Share', action: 'share' },
|
|
69
|
-
],
|
|
70
|
-
},
|
|
71
|
-
};
|
|
72
|
-
render(<DashboardRenderer schema={schema} />);
|
|
73
|
-
|
|
74
|
-
expect(screen.getByText('Export')).toBeInTheDocument();
|
|
75
|
-
expect(screen.getByText('Share')).toBeInTheDocument();
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('should render recordCount badge when prop provided', () => {
|
|
79
|
-
const schema: DashboardSchema = {
|
|
80
|
-
...baseSchema,
|
|
81
|
-
header: {},
|
|
82
|
-
};
|
|
83
|
-
const onRefresh = vi.fn();
|
|
84
|
-
render(<DashboardRenderer schema={schema} onRefresh={onRefresh} recordCount={1234} />);
|
|
85
|
-
|
|
86
|
-
expect(screen.getByText('1,234 records')).toBeInTheDocument();
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('should set data-user-actions attribute when userActions provided', () => {
|
|
90
|
-
const schema: DashboardSchema = {
|
|
91
|
-
...baseSchema,
|
|
92
|
-
header: {},
|
|
93
|
-
};
|
|
94
|
-
const userActions = { sort: true, search: false, filter: true };
|
|
95
|
-
const { container } = render(
|
|
96
|
-
<DashboardRenderer schema={schema} userActions={userActions} />
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
const root = container.firstElementChild as HTMLElement;
|
|
100
|
-
expect(root.getAttribute('data-user-actions')).toBe(JSON.stringify(userActions));
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it('should not render header section when schema.header is undefined', () => {
|
|
104
|
-
const schema: DashboardSchema = {
|
|
105
|
-
...baseSchema,
|
|
106
|
-
header: undefined,
|
|
107
|
-
};
|
|
108
|
-
render(<DashboardRenderer schema={schema} />);
|
|
109
|
-
|
|
110
|
-
// Neither title nor description should appear in a header context
|
|
111
|
-
// The title text itself should not be rendered since there is no header
|
|
112
|
-
expect(screen.queryByText('Sales Dashboard')).not.toBeInTheDocument();
|
|
113
|
-
});
|
|
114
|
-
});
|