@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.
- package/CHANGELOG.md +28 -0
- package/README.md +21 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1162 -939
- package/dist/index.umd.cjs +4 -4
- package/dist/packages/plugin-dashboard/src/DashboardConfigPanel.d.ts.map +1 -0
- package/dist/packages/plugin-dashboard/src/DashboardConfigPanel.stories.d.ts.map +1 -0
- package/dist/packages/plugin-dashboard/src/DashboardGridLayout.d.ts.map +1 -0
- package/dist/{src → packages/plugin-dashboard/src}/DashboardRenderer.d.ts +5 -0
- package/dist/packages/plugin-dashboard/src/DashboardRenderer.d.ts.map +1 -0
- package/dist/packages/plugin-dashboard/src/DashboardRenderer.stories.d.ts.map +1 -0
- package/dist/packages/plugin-dashboard/src/DashboardWithConfig.d.ts.map +1 -0
- package/dist/{src → packages/plugin-dashboard/src}/MetricCard.d.ts +4 -0
- package/dist/packages/plugin-dashboard/src/MetricCard.d.ts.map +1 -0
- package/dist/packages/plugin-dashboard/src/MetricWidget.d.ts +31 -0
- package/dist/packages/plugin-dashboard/src/MetricWidget.d.ts.map +1 -0
- package/dist/packages/plugin-dashboard/src/ObjectDataTable.d.ts.map +1 -0
- package/dist/packages/plugin-dashboard/src/ObjectMetricWidget.d.ts +59 -0
- package/dist/packages/plugin-dashboard/src/ObjectMetricWidget.d.ts.map +1 -0
- package/dist/packages/plugin-dashboard/src/ObjectPivotTable.d.ts.map +1 -0
- package/dist/packages/plugin-dashboard/src/PivotTable.d.ts.map +1 -0
- package/dist/packages/plugin-dashboard/src/WidgetConfigPanel.d.ts.map +1 -0
- package/dist/{src → packages/plugin-dashboard/src}/index.d.ts +4 -2
- package/dist/packages/plugin-dashboard/src/index.d.ts.map +1 -0
- package/dist/packages/plugin-dashboard/src/utils.d.ts.map +1 -0
- package/package.json +44 -11
- package/.turbo/turbo-build.log +0 -34
- package/dist/src/DashboardConfigPanel.d.ts.map +0 -1
- package/dist/src/DashboardConfigPanel.stories.d.ts.map +0 -1
- package/dist/src/DashboardGridLayout.d.ts.map +0 -1
- package/dist/src/DashboardRenderer.d.ts.map +0 -1
- package/dist/src/DashboardRenderer.stories.d.ts.map +0 -1
- package/dist/src/DashboardWithConfig.d.ts.map +0 -1
- package/dist/src/MetricCard.d.ts.map +0 -1
- package/dist/src/MetricWidget.d.ts +0 -24
- package/dist/src/MetricWidget.d.ts.map +0 -1
- package/dist/src/ObjectDataTable.d.ts.map +0 -1
- package/dist/src/ObjectPivotTable.d.ts.map +0 -1
- package/dist/src/PivotTable.d.ts.map +0 -1
- package/dist/src/WidgetConfigPanel.d.ts.map +0 -1
- package/dist/src/index.d.ts.map +0 -1
- package/dist/src/utils.d.ts.map +0 -1
- 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 -445
- package/src/DashboardWithConfig.tsx +0 -211
- package/src/MetricCard.tsx +0 -82
- package/src/MetricWidget.tsx +0 -76
- package/src/ObjectDataTable.tsx +0 -226
- 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 -1283
- package/src/__tests__/DashboardWithConfig.test.tsx +0 -276
- package/src/__tests__/MetricCard.test.tsx +0 -82
- package/src/__tests__/ObjectDataTable.test.tsx +0 -211
- 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 -214
- package/src/utils.ts +0 -17
- package/tsconfig.json +0 -19
- package/vite.config.ts +0 -63
- package/vitest.config.ts +0 -9
- package/vitest.setup.tsx +0 -18
- /package/dist/{src → packages/plugin-dashboard/src}/DashboardConfigPanel.d.ts +0 -0
- /package/dist/{src → packages/plugin-dashboard/src}/DashboardConfigPanel.stories.d.ts +0 -0
- /package/dist/{src → packages/plugin-dashboard/src}/DashboardGridLayout.d.ts +0 -0
- /package/dist/{src → packages/plugin-dashboard/src}/DashboardRenderer.stories.d.ts +0 -0
- /package/dist/{src → packages/plugin-dashboard/src}/DashboardWithConfig.d.ts +0 -0
- /package/dist/{src → packages/plugin-dashboard/src}/ObjectDataTable.d.ts +0 -0
- /package/dist/{src → packages/plugin-dashboard/src}/ObjectPivotTable.d.ts +0 -0
- /package/dist/{src → packages/plugin-dashboard/src}/PivotTable.d.ts +0 -0
- /package/dist/{src → packages/plugin-dashboard/src}/WidgetConfigPanel.d.ts +0 -0
- /package/dist/{src → packages/plugin-dashboard/src}/utils.d.ts +0 -0
|
@@ -1,206 +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 { describe, it, expect, vi } from 'vitest';
|
|
10
|
-
import { render, screen, fireEvent } from '@testing-library/react';
|
|
11
|
-
import { DashboardConfigPanel } from '../DashboardConfigPanel';
|
|
12
|
-
|
|
13
|
-
const defaultConfig = {
|
|
14
|
-
columns: 3,
|
|
15
|
-
gap: 4,
|
|
16
|
-
rowHeight: '120',
|
|
17
|
-
refreshInterval: '0',
|
|
18
|
-
title: 'My Dashboard',
|
|
19
|
-
showDescription: true,
|
|
20
|
-
theme: 'auto',
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
describe('DashboardConfigPanel', () => {
|
|
24
|
-
it('should render nothing when closed', () => {
|
|
25
|
-
const { container } = render(
|
|
26
|
-
<DashboardConfigPanel
|
|
27
|
-
open={false}
|
|
28
|
-
onClose={vi.fn()}
|
|
29
|
-
config={defaultConfig}
|
|
30
|
-
onSave={vi.fn()}
|
|
31
|
-
/>,
|
|
32
|
-
);
|
|
33
|
-
expect(container.innerHTML).toBe('');
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('should render panel when open', () => {
|
|
37
|
-
render(
|
|
38
|
-
<DashboardConfigPanel
|
|
39
|
-
open={true}
|
|
40
|
-
onClose={vi.fn()}
|
|
41
|
-
config={defaultConfig}
|
|
42
|
-
onSave={vi.fn()}
|
|
43
|
-
/>,
|
|
44
|
-
);
|
|
45
|
-
expect(screen.getByTestId('config-panel')).toBeDefined();
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('should display dashboard breadcrumb', () => {
|
|
49
|
-
render(
|
|
50
|
-
<DashboardConfigPanel
|
|
51
|
-
open={true}
|
|
52
|
-
onClose={vi.fn()}
|
|
53
|
-
config={defaultConfig}
|
|
54
|
-
onSave={vi.fn()}
|
|
55
|
-
/>,
|
|
56
|
-
);
|
|
57
|
-
expect(screen.getByText('Dashboard')).toBeDefined();
|
|
58
|
-
expect(screen.getByText('Configuration')).toBeDefined();
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('should display layout section fields', () => {
|
|
62
|
-
render(
|
|
63
|
-
<DashboardConfigPanel
|
|
64
|
-
open={true}
|
|
65
|
-
onClose={vi.fn()}
|
|
66
|
-
config={defaultConfig}
|
|
67
|
-
onSave={vi.fn()}
|
|
68
|
-
/>,
|
|
69
|
-
);
|
|
70
|
-
expect(screen.getByText('Layout')).toBeDefined();
|
|
71
|
-
expect(screen.getByText('Columns')).toBeDefined();
|
|
72
|
-
expect(screen.getByText('Gap')).toBeDefined();
|
|
73
|
-
expect(screen.getByText('Row height')).toBeDefined();
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it('should display data section', () => {
|
|
77
|
-
render(
|
|
78
|
-
<DashboardConfigPanel
|
|
79
|
-
open={true}
|
|
80
|
-
onClose={vi.fn()}
|
|
81
|
-
config={defaultConfig}
|
|
82
|
-
onSave={vi.fn()}
|
|
83
|
-
/>,
|
|
84
|
-
);
|
|
85
|
-
expect(screen.getByText('Data')).toBeDefined();
|
|
86
|
-
expect(screen.getByText('Refresh interval')).toBeDefined();
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('should have appearance section collapsed by default', () => {
|
|
90
|
-
render(
|
|
91
|
-
<DashboardConfigPanel
|
|
92
|
-
open={true}
|
|
93
|
-
onClose={vi.fn()}
|
|
94
|
-
config={defaultConfig}
|
|
95
|
-
onSave={vi.fn()}
|
|
96
|
-
/>,
|
|
97
|
-
);
|
|
98
|
-
expect(screen.getByText('Appearance')).toBeDefined();
|
|
99
|
-
// Title field should be hidden since appearance is collapsed by default
|
|
100
|
-
// Note: 'My Dashboard' is the title field value from the config, not the section title.
|
|
101
|
-
// The input label 'Title' should not be visible while collapsed
|
|
102
|
-
expect(screen.queryByText('Show description')).toBeNull();
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it('should expand appearance section on click', () => {
|
|
106
|
-
render(
|
|
107
|
-
<DashboardConfigPanel
|
|
108
|
-
open={true}
|
|
109
|
-
onClose={vi.fn()}
|
|
110
|
-
config={defaultConfig}
|
|
111
|
-
onSave={vi.fn()}
|
|
112
|
-
/>,
|
|
113
|
-
);
|
|
114
|
-
// Click to expand
|
|
115
|
-
fireEvent.click(screen.getByTestId('section-header-appearance'));
|
|
116
|
-
expect(screen.getByText('Show description')).toBeDefined();
|
|
117
|
-
expect(screen.getByText('Theme')).toBeDefined();
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it('should call onClose when close button clicked', () => {
|
|
121
|
-
const onClose = vi.fn();
|
|
122
|
-
render(
|
|
123
|
-
<DashboardConfigPanel
|
|
124
|
-
open={true}
|
|
125
|
-
onClose={onClose}
|
|
126
|
-
config={defaultConfig}
|
|
127
|
-
onSave={vi.fn()}
|
|
128
|
-
/>,
|
|
129
|
-
);
|
|
130
|
-
fireEvent.click(screen.getByTestId('config-panel-close'));
|
|
131
|
-
expect(onClose).toHaveBeenCalledTimes(1);
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it('should show save/discard footer after editing a field', () => {
|
|
135
|
-
render(
|
|
136
|
-
<DashboardConfigPanel
|
|
137
|
-
open={true}
|
|
138
|
-
onClose={vi.fn()}
|
|
139
|
-
config={defaultConfig}
|
|
140
|
-
onSave={vi.fn()}
|
|
141
|
-
/>,
|
|
142
|
-
);
|
|
143
|
-
// Edit the row height input
|
|
144
|
-
const rowHeightInput = screen.getByTestId('config-field-rowHeight');
|
|
145
|
-
fireEvent.change(rowHeightInput, { target: { value: '200' } });
|
|
146
|
-
expect(screen.getByTestId('config-panel-footer')).toBeDefined();
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it('should call onSave with updated draft', () => {
|
|
150
|
-
const onSave = vi.fn();
|
|
151
|
-
render(
|
|
152
|
-
<DashboardConfigPanel
|
|
153
|
-
open={true}
|
|
154
|
-
onClose={vi.fn()}
|
|
155
|
-
config={defaultConfig}
|
|
156
|
-
onSave={onSave}
|
|
157
|
-
/>,
|
|
158
|
-
);
|
|
159
|
-
// Modify a field
|
|
160
|
-
fireEvent.change(screen.getByTestId('config-field-rowHeight'), {
|
|
161
|
-
target: { value: '200' },
|
|
162
|
-
});
|
|
163
|
-
// Save
|
|
164
|
-
fireEvent.click(screen.getByTestId('config-panel-save'));
|
|
165
|
-
expect(onSave).toHaveBeenCalledTimes(1);
|
|
166
|
-
const savedDraft = onSave.mock.calls[0][0];
|
|
167
|
-
expect(savedDraft.rowHeight).toBe('200');
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
it('should revert changes on discard', () => {
|
|
171
|
-
render(
|
|
172
|
-
<DashboardConfigPanel
|
|
173
|
-
open={true}
|
|
174
|
-
onClose={vi.fn()}
|
|
175
|
-
config={defaultConfig}
|
|
176
|
-
onSave={vi.fn()}
|
|
177
|
-
/>,
|
|
178
|
-
);
|
|
179
|
-
const input = screen.getByTestId('config-field-rowHeight') as HTMLInputElement;
|
|
180
|
-
fireEvent.change(input, { target: { value: '999' } });
|
|
181
|
-
expect(input.value).toBe('999');
|
|
182
|
-
|
|
183
|
-
// Discard
|
|
184
|
-
fireEvent.click(screen.getByTestId('config-panel-discard'));
|
|
185
|
-
|
|
186
|
-
// Footer should disappear (no longer dirty)
|
|
187
|
-
expect(screen.queryByTestId('config-panel-footer')).toBeNull();
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it('should call onFieldChange for live updates', () => {
|
|
191
|
-
const onFieldChange = vi.fn();
|
|
192
|
-
render(
|
|
193
|
-
<DashboardConfigPanel
|
|
194
|
-
open={true}
|
|
195
|
-
onClose={vi.fn()}
|
|
196
|
-
config={defaultConfig}
|
|
197
|
-
onSave={vi.fn()}
|
|
198
|
-
onFieldChange={onFieldChange}
|
|
199
|
-
/>,
|
|
200
|
-
);
|
|
201
|
-
fireEvent.change(screen.getByTestId('config-field-rowHeight'), {
|
|
202
|
-
target: { value: '150' },
|
|
203
|
-
});
|
|
204
|
-
expect(onFieldChange).toHaveBeenCalledWith('rowHeight', '150');
|
|
205
|
-
});
|
|
206
|
-
});
|
|
@@ -1,199 +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 { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
10
|
-
import { render, screen, fireEvent } from '@testing-library/react';
|
|
11
|
-
import { DashboardGridLayout } from '../DashboardGridLayout';
|
|
12
|
-
import type { DashboardSchema } from '@object-ui/types';
|
|
13
|
-
|
|
14
|
-
// Mock localStorage
|
|
15
|
-
const localStorageMock = (() => {
|
|
16
|
-
let store: Record<string, string> = {};
|
|
17
|
-
return {
|
|
18
|
-
getItem: (key: string) => store[key] || null,
|
|
19
|
-
setItem: (key: string, value: string) => { store[key] = value; },
|
|
20
|
-
clear: () => { store = {}; },
|
|
21
|
-
removeItem: (key: string) => { delete store[key]; },
|
|
22
|
-
};
|
|
23
|
-
})();
|
|
24
|
-
|
|
25
|
-
Object.defineProperty(window, 'localStorage', { value: localStorageMock });
|
|
26
|
-
|
|
27
|
-
// Mock react-grid-layout
|
|
28
|
-
vi.mock('react-grid-layout', () => ({
|
|
29
|
-
Responsive: ({ children }: any) => <div data-testid="grid-layout">{children}</div>,
|
|
30
|
-
WidthProvider: (Component: any) => Component,
|
|
31
|
-
}));
|
|
32
|
-
|
|
33
|
-
describe('DashboardGridLayout', () => {
|
|
34
|
-
beforeEach(() => {
|
|
35
|
-
localStorageMock.clear();
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
const mockSchema: DashboardSchema = {
|
|
39
|
-
type: 'dashboard',
|
|
40
|
-
name: 'test_dashboard',
|
|
41
|
-
title: 'Test Dashboard',
|
|
42
|
-
widgets: [
|
|
43
|
-
{
|
|
44
|
-
id: 'widget-1',
|
|
45
|
-
type: 'metric-card',
|
|
46
|
-
title: 'Total Sales',
|
|
47
|
-
layout: { x: 0, y: 0, w: 3, h: 2 },
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
id: 'widget-2',
|
|
51
|
-
type: 'bar',
|
|
52
|
-
title: 'Revenue by Month',
|
|
53
|
-
layout: { x: 3, y: 0, w: 6, h: 4 },
|
|
54
|
-
},
|
|
55
|
-
],
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
it('should render without crashing', () => {
|
|
59
|
-
const { container } = render(<DashboardGridLayout schema={mockSchema} />);
|
|
60
|
-
expect(container).toBeTruthy();
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('should render dashboard title', () => {
|
|
64
|
-
render(<DashboardGridLayout schema={mockSchema} />);
|
|
65
|
-
expect(screen.getByText('Test Dashboard')).toBeInTheDocument();
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('should render all widgets', () => {
|
|
69
|
-
render(<DashboardGridLayout schema={mockSchema} />);
|
|
70
|
-
|
|
71
|
-
expect(screen.getAllByText('Total Sales').length).toBeGreaterThan(0);
|
|
72
|
-
expect(screen.getByText('Revenue by Month')).toBeInTheDocument();
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('should render edit mode button', () => {
|
|
76
|
-
render(<DashboardGridLayout schema={mockSchema} />);
|
|
77
|
-
|
|
78
|
-
const editButton = screen.getByRole('button', { name: /edit/i });
|
|
79
|
-
expect(editButton).toBeInTheDocument();
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it('should toggle edit mode when edit button is clicked', () => {
|
|
83
|
-
render(<DashboardGridLayout schema={mockSchema} />);
|
|
84
|
-
|
|
85
|
-
const editButton = screen.getByRole('button', { name: /edit/i });
|
|
86
|
-
fireEvent.click(editButton);
|
|
87
|
-
|
|
88
|
-
// In edit mode, should show Save and Cancel buttons
|
|
89
|
-
expect(screen.getByRole('button', { name: /save/i })).toBeInTheDocument();
|
|
90
|
-
expect(screen.getByRole('button', { name: /cancel/i })).toBeInTheDocument();
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it('should save layout to localStorage when save button is clicked', () => {
|
|
94
|
-
render(<DashboardGridLayout schema={mockSchema} persistLayoutKey="test-layout" />);
|
|
95
|
-
|
|
96
|
-
const editButton = screen.getByRole('button', { name: /edit/i });
|
|
97
|
-
fireEvent.click(editButton);
|
|
98
|
-
|
|
99
|
-
const saveButton = screen.getByRole('button', { name: /save/i });
|
|
100
|
-
fireEvent.click(saveButton);
|
|
101
|
-
|
|
102
|
-
// Check that layout was saved to localStorage
|
|
103
|
-
const saved = localStorageMock.getItem('test-layout');
|
|
104
|
-
expect(saved).toBeTruthy();
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('should restore layout from localStorage', () => {
|
|
108
|
-
const savedLayout = {
|
|
109
|
-
lg: [
|
|
110
|
-
{ i: 'widget-1', x: 0, y: 0, w: 3, h: 2 },
|
|
111
|
-
{ i: 'widget-2', x: 3, y: 0, w: 6, h: 4 },
|
|
112
|
-
],
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
localStorageMock.setItem('test-layout', JSON.stringify(savedLayout));
|
|
116
|
-
|
|
117
|
-
render(<DashboardGridLayout schema={mockSchema} persistLayoutKey="test-layout" />);
|
|
118
|
-
|
|
119
|
-
// Component should render with saved layout
|
|
120
|
-
expect(screen.getByText('Test Dashboard')).toBeInTheDocument();
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it('should call onLayoutChange when layout changes', () => {
|
|
124
|
-
const onLayoutChange = vi.fn();
|
|
125
|
-
render(<DashboardGridLayout schema={mockSchema} onLayoutChange={onLayoutChange} />);
|
|
126
|
-
|
|
127
|
-
// Trigger layout change (this would normally happen through drag/drop)
|
|
128
|
-
// In our mock, we can't easily trigger this, but we verify the callback exists
|
|
129
|
-
expect(onLayoutChange).toBeDefined();
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it('should cancel edit mode when cancel button is clicked', () => {
|
|
133
|
-
render(<DashboardGridLayout schema={mockSchema} />);
|
|
134
|
-
|
|
135
|
-
const editButton = screen.getByRole('button', { name: /edit/i });
|
|
136
|
-
fireEvent.click(editButton);
|
|
137
|
-
|
|
138
|
-
const cancelButton = screen.getByRole('button', { name: /cancel/i });
|
|
139
|
-
fireEvent.click(cancelButton);
|
|
140
|
-
|
|
141
|
-
// Should exit edit mode
|
|
142
|
-
expect(screen.getByRole('button', { name: /edit/i })).toBeInTheDocument();
|
|
143
|
-
expect(screen.queryByRole('button', { name: /save/i })).not.toBeInTheDocument();
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
it('should reset layout when reset button is clicked', () => {
|
|
147
|
-
render(<DashboardGridLayout schema={mockSchema} />);
|
|
148
|
-
|
|
149
|
-
const editButton = screen.getByRole('button', { name: /edit/i });
|
|
150
|
-
fireEvent.click(editButton);
|
|
151
|
-
|
|
152
|
-
// Look for reset button (might be in a dropdown or menu)
|
|
153
|
-
const buttons = screen.getAllByRole('button');
|
|
154
|
-
const resetButton = buttons.find(btn => btn.textContent?.includes('Reset'));
|
|
155
|
-
|
|
156
|
-
if (resetButton) {
|
|
157
|
-
fireEvent.click(resetButton);
|
|
158
|
-
}
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
it('should render grid layout container', () => {
|
|
162
|
-
render(<DashboardGridLayout schema={mockSchema} />);
|
|
163
|
-
|
|
164
|
-
const gridLayout = screen.getByTestId('grid-layout');
|
|
165
|
-
expect(gridLayout).toBeInTheDocument();
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
it('should handle empty widgets array', () => {
|
|
169
|
-
const emptySchema: DashboardSchema = {
|
|
170
|
-
type: 'dashboard',
|
|
171
|
-
name: 'empty_dashboard',
|
|
172
|
-
title: 'Empty Dashboard',
|
|
173
|
-
widgets: [],
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
const { container } = render(<DashboardGridLayout schema={emptySchema} />);
|
|
177
|
-
expect(container).toBeTruthy();
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
it('should apply custom className', () => {
|
|
181
|
-
const { container } = render(
|
|
182
|
-
<DashboardGridLayout schema={mockSchema} className="custom-class" />
|
|
183
|
-
);
|
|
184
|
-
|
|
185
|
-
const dashboardContainer = container.querySelector('.custom-class');
|
|
186
|
-
expect(dashboardContainer).toBeTruthy();
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
it('should render drag handles in edit mode', () => {
|
|
190
|
-
const { container } = render(<DashboardGridLayout schema={mockSchema} />);
|
|
191
|
-
|
|
192
|
-
const editButton = screen.getByRole('button', { name: /edit/i });
|
|
193
|
-
fireEvent.click(editButton);
|
|
194
|
-
|
|
195
|
-
// In edit mode, widgets should have drag handles (GripVertical icons)
|
|
196
|
-
const gripIcons = container.querySelectorAll('svg');
|
|
197
|
-
expect(gripIcons.length).toBeGreaterThan(0);
|
|
198
|
-
});
|
|
199
|
-
});
|
|
@@ -1,124 +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 { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
10
|
-
import { render, screen, fireEvent, act } from '@testing-library/react';
|
|
11
|
-
import { DashboardRenderer } from '../DashboardRenderer';
|
|
12
|
-
import type { DashboardSchema } from '@object-ui/types';
|
|
13
|
-
|
|
14
|
-
describe('DashboardRenderer auto-refresh', () => {
|
|
15
|
-
beforeEach(() => {
|
|
16
|
-
vi.useFakeTimers();
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
afterEach(() => {
|
|
20
|
-
vi.useRealTimers();
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
const mockSchema: DashboardSchema = {
|
|
24
|
-
type: 'dashboard',
|
|
25
|
-
name: 'test_dashboard',
|
|
26
|
-
title: 'Test Dashboard',
|
|
27
|
-
widgets: [],
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
it('should not render refresh button when onRefresh is not provided', () => {
|
|
31
|
-
render(<DashboardRenderer schema={mockSchema} />);
|
|
32
|
-
expect(screen.queryByLabelText('Refresh dashboard')).not.toBeInTheDocument();
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('should render refresh button when onRefresh is provided', () => {
|
|
36
|
-
const onRefresh = vi.fn();
|
|
37
|
-
render(<DashboardRenderer schema={mockSchema} onRefresh={onRefresh} />);
|
|
38
|
-
expect(screen.getByLabelText('Refresh dashboard')).toBeInTheDocument();
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it('should call onRefresh when refresh button is clicked', () => {
|
|
42
|
-
const onRefresh = vi.fn();
|
|
43
|
-
render(<DashboardRenderer schema={mockSchema} onRefresh={onRefresh} />);
|
|
44
|
-
|
|
45
|
-
const button = screen.getByLabelText('Refresh dashboard');
|
|
46
|
-
fireEvent.click(button);
|
|
47
|
-
|
|
48
|
-
expect(onRefresh).toHaveBeenCalledTimes(1);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('should auto-refresh at the configured interval', () => {
|
|
52
|
-
const onRefresh = vi.fn();
|
|
53
|
-
const schemaWithRefresh: DashboardSchema = {
|
|
54
|
-
...mockSchema,
|
|
55
|
-
refreshInterval: 30, // 30 seconds
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
render(<DashboardRenderer schema={schemaWithRefresh} onRefresh={onRefresh} />);
|
|
59
|
-
|
|
60
|
-
expect(onRefresh).not.toHaveBeenCalled();
|
|
61
|
-
|
|
62
|
-
// Advance past one interval
|
|
63
|
-
act(() => {
|
|
64
|
-
vi.advanceTimersByTime(30_000);
|
|
65
|
-
});
|
|
66
|
-
expect(onRefresh).toHaveBeenCalledTimes(1);
|
|
67
|
-
|
|
68
|
-
// Advance past another interval
|
|
69
|
-
act(() => {
|
|
70
|
-
vi.advanceTimersByTime(30_000);
|
|
71
|
-
});
|
|
72
|
-
expect(onRefresh).toHaveBeenCalledTimes(2);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('should not auto-refresh when refreshInterval is 0', () => {
|
|
76
|
-
const onRefresh = vi.fn();
|
|
77
|
-
const schemaWithZeroInterval: DashboardSchema = {
|
|
78
|
-
...mockSchema,
|
|
79
|
-
refreshInterval: 0,
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
render(<DashboardRenderer schema={schemaWithZeroInterval} onRefresh={onRefresh} />);
|
|
83
|
-
|
|
84
|
-
act(() => {
|
|
85
|
-
vi.advanceTimersByTime(60_000);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
expect(onRefresh).not.toHaveBeenCalled();
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it('should not auto-refresh when onRefresh is not provided', () => {
|
|
92
|
-
const schemaWithRefresh: DashboardSchema = {
|
|
93
|
-
...mockSchema,
|
|
94
|
-
refreshInterval: 10,
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
// Should not throw
|
|
98
|
-
render(<DashboardRenderer schema={schemaWithRefresh} />);
|
|
99
|
-
|
|
100
|
-
act(() => {
|
|
101
|
-
vi.advanceTimersByTime(30_000);
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it('should clean up interval on unmount', () => {
|
|
106
|
-
const onRefresh = vi.fn();
|
|
107
|
-
const schemaWithRefresh: DashboardSchema = {
|
|
108
|
-
...mockSchema,
|
|
109
|
-
refreshInterval: 5,
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
const { unmount } = render(
|
|
113
|
-
<DashboardRenderer schema={schemaWithRefresh} onRefresh={onRefresh} />
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
unmount();
|
|
117
|
-
|
|
118
|
-
act(() => {
|
|
119
|
-
vi.advanceTimersByTime(30_000);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
expect(onRefresh).not.toHaveBeenCalled();
|
|
123
|
-
});
|
|
124
|
-
});
|