@object-ui/plugin-timeline 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 (33) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +20 -0
  3. package/dist/index.d.ts +1 -1
  4. package/dist/index.js +1041 -751
  5. package/dist/index.umd.cjs +31 -6
  6. package/dist/packages/plugin-timeline/src/ObjectTimeline.d.ts.map +1 -0
  7. package/dist/packages/plugin-timeline/src/ObjectTimeline.stories.d.ts.map +1 -0
  8. package/dist/packages/plugin-timeline/src/index.d.ts.map +1 -0
  9. package/dist/packages/plugin-timeline/src/renderer.d.ts.map +1 -0
  10. package/package.json +36 -13
  11. package/.turbo/turbo-build.log +0 -22
  12. package/dist/src/ObjectTimeline.d.ts.map +0 -1
  13. package/dist/src/ObjectTimeline.stories.d.ts.map +0 -1
  14. package/dist/src/index.d.ts.map +0 -1
  15. package/dist/src/renderer.d.ts.map +0 -1
  16. package/src/ObjectTimeline.msw.test.tsx +0 -80
  17. package/src/ObjectTimeline.stories.tsx +0 -113
  18. package/src/ObjectTimeline.test.tsx +0 -138
  19. package/src/ObjectTimeline.tsx +0 -241
  20. package/src/__tests__/ObjectTimeline.spec-config.test.tsx +0 -232
  21. package/src/__tests__/renderer.test.tsx +0 -295
  22. package/src/__tests__/timeline-variants.test.tsx +0 -150
  23. package/src/index.test.tsx +0 -24
  24. package/src/index.tsx +0 -334
  25. package/src/renderer.tsx +0 -477
  26. package/tsconfig.json +0 -18
  27. package/vite.config.ts +0 -63
  28. package/vitest.config.ts +0 -13
  29. package/vitest.setup.ts +0 -1
  30. /package/dist/{src → packages/plugin-timeline/src}/ObjectTimeline.d.ts +0 -0
  31. /package/dist/{src → packages/plugin-timeline/src}/ObjectTimeline.stories.d.ts +0 -0
  32. /package/dist/{src → packages/plugin-timeline/src}/index.d.ts +0 -0
  33. /package/dist/{src → packages/plugin-timeline/src}/renderer.d.ts +0 -0
@@ -1,232 +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 } from '@testing-library/react';
11
- import '@testing-library/jest-dom';
12
- import { ObjectTimeline } from '../ObjectTimeline';
13
-
14
- // Mock dependencies
15
- vi.mock('@object-ui/react', () => ({
16
- useDataScope: () => undefined,
17
- useNavigationOverlay: () => ({
18
- isOverlay: false,
19
- handleClick: vi.fn(),
20
- selectedRecord: null,
21
- isOpen: false,
22
- close: vi.fn(),
23
- setIsOpen: vi.fn(),
24
- mode: 'page' as const,
25
- width: undefined,
26
- view: undefined,
27
- open: vi.fn(),
28
- }),
29
- }));
30
-
31
- vi.mock('@object-ui/components', () => ({
32
- NavigationOverlay: () => null,
33
- }));
34
-
35
- vi.mock('@object-ui/mobile', () => ({
36
- usePullToRefresh: () => ({
37
- ref: { current: null },
38
- isRefreshing: false,
39
- pullDistance: 0,
40
- }),
41
- }));
42
-
43
- // Mock the TimelineRenderer to inspect the schema it receives
44
- const mockTimelineRenderer = vi.fn(() => <div data-testid="timeline-renderer" />);
45
- vi.mock('../renderer', () => ({
46
- TimelineRenderer: (props: any) => mockTimelineRenderer(props),
47
- }));
48
-
49
- describe('ObjectTimeline Spec Config', () => {
50
- beforeEach(() => {
51
- mockTimelineRenderer.mockClear();
52
- });
53
-
54
- const mockData = [
55
- { id: '1', name: 'Event A', start_date: '2024-01-01', end_date: '2024-01-15', category: 'work', priority_color: 'red' },
56
- { id: '2', name: 'Event B', start_date: '2024-02-01', end_date: '2024-02-28', category: 'personal', priority_color: 'blue' },
57
- ];
58
-
59
- describe('nested timeline config', () => {
60
- it('uses timeline.startDateField to resolve start dates', () => {
61
- const schema: any = {
62
- type: 'timeline' as const,
63
- objectName: 'events',
64
- timeline: { startDateField: 'start_date', titleField: 'name' },
65
- };
66
- render(<ObjectTimeline schema={schema} data={mockData} />);
67
- expect(mockTimelineRenderer).toHaveBeenCalled();
68
- const renderedSchema = mockTimelineRenderer.mock.calls[0][0].schema;
69
- expect(renderedSchema.items[0].time).toBe('2024-01-01');
70
- expect(renderedSchema.items[0].startDate).toBe('2024-01-01');
71
- });
72
-
73
- it('uses timeline.titleField to resolve titles', () => {
74
- const schema: any = {
75
- type: 'timeline' as const,
76
- objectName: 'events',
77
- timeline: { startDateField: 'start_date', titleField: 'name' },
78
- };
79
- render(<ObjectTimeline schema={schema} data={mockData} />);
80
- const renderedSchema = mockTimelineRenderer.mock.calls[0][0].schema;
81
- expect(renderedSchema.items[0].title).toBe('Event A');
82
- expect(renderedSchema.items[1].title).toBe('Event B');
83
- });
84
-
85
- it('uses timeline.endDateField to resolve end dates', () => {
86
- const schema: any = {
87
- type: 'timeline' as const,
88
- objectName: 'events',
89
- timeline: { startDateField: 'start_date', endDateField: 'end_date', titleField: 'name' },
90
- };
91
- render(<ObjectTimeline schema={schema} data={mockData} />);
92
- const renderedSchema = mockTimelineRenderer.mock.calls[0][0].schema;
93
- expect(renderedSchema.items[0].endDate).toBe('2024-01-15');
94
- });
95
-
96
- it('uses timeline.groupByField to add group property', () => {
97
- const schema: any = {
98
- type: 'timeline' as const,
99
- objectName: 'events',
100
- timeline: { startDateField: 'start_date', titleField: 'name', groupByField: 'category' },
101
- };
102
- render(<ObjectTimeline schema={schema} data={mockData} />);
103
- const renderedSchema = mockTimelineRenderer.mock.calls[0][0].schema;
104
- expect(renderedSchema.items[0].group).toBe('work');
105
- expect(renderedSchema.items[1].group).toBe('personal');
106
- });
107
-
108
- it('uses timeline.colorField to add color property', () => {
109
- const schema: any = {
110
- type: 'timeline' as const,
111
- objectName: 'events',
112
- timeline: { startDateField: 'start_date', titleField: 'name', colorField: 'priority_color' },
113
- };
114
- render(<ObjectTimeline schema={schema} data={mockData} />);
115
- const renderedSchema = mockTimelineRenderer.mock.calls[0][0].schema;
116
- expect(renderedSchema.items[0].color).toBe('red');
117
- expect(renderedSchema.items[1].color).toBe('blue');
118
- });
119
-
120
- it('uses timeline.scale to set timeScale on rendered schema', () => {
121
- const schema: any = {
122
- type: 'timeline' as const,
123
- objectName: 'events',
124
- timeline: { startDateField: 'start_date', titleField: 'name', scale: 'month' },
125
- };
126
- render(<ObjectTimeline schema={schema} data={mockData} />);
127
- const renderedSchema = mockTimelineRenderer.mock.calls[0][0].schema;
128
- expect(renderedSchema.timeScale).toBe('month');
129
- });
130
-
131
- it('timeline.scale supports all valid values', () => {
132
- const scales = ['hour', 'day', 'week', 'month', 'quarter', 'year'] as const;
133
- scales.forEach((scale) => {
134
- mockTimelineRenderer.mockClear();
135
- const schema: any = {
136
- type: 'timeline' as const,
137
- objectName: 'events',
138
- timeline: { startDateField: 'start_date', titleField: 'name', scale },
139
- };
140
- render(<ObjectTimeline schema={schema} data={mockData} />);
141
- const renderedSchema = mockTimelineRenderer.mock.calls[0][0].schema;
142
- expect(renderedSchema.timeScale).toBe(scale);
143
- });
144
- });
145
- });
146
-
147
- describe('backward compatibility', () => {
148
- it('falls back to flat startDateField when timeline config is not set', () => {
149
- const schema: any = {
150
- type: 'timeline' as const,
151
- objectName: 'events',
152
- startDateField: 'start_date',
153
- titleField: 'name',
154
- };
155
- render(<ObjectTimeline schema={schema} data={mockData} />);
156
- const renderedSchema = mockTimelineRenderer.mock.calls[0][0].schema;
157
- expect(renderedSchema.items[0].time).toBe('2024-01-01');
158
- });
159
-
160
- it('falls back to legacy dateField when startDateField is not set', () => {
161
- const data = [{ id: '1', name: 'Event', legacy_date: '2024-03-15' }];
162
- const schema: any = {
163
- type: 'timeline' as const,
164
- objectName: 'events',
165
- dateField: 'legacy_date',
166
- titleField: 'name',
167
- };
168
- render(<ObjectTimeline schema={schema} data={data} />);
169
- const renderedSchema = mockTimelineRenderer.mock.calls[0][0].schema;
170
- expect(renderedSchema.items[0].time).toBe('2024-03-15');
171
- });
172
-
173
- it('falls back to flat groupByField when timeline.groupByField is not set', () => {
174
- const schema: any = {
175
- type: 'timeline' as const,
176
- objectName: 'events',
177
- startDateField: 'start_date',
178
- titleField: 'name',
179
- groupByField: 'category',
180
- };
181
- render(<ObjectTimeline schema={schema} data={mockData} />);
182
- const renderedSchema = mockTimelineRenderer.mock.calls[0][0].schema;
183
- expect(renderedSchema.items[0].group).toBe('work');
184
- });
185
-
186
- it('falls back to flat colorField when timeline.colorField is not set', () => {
187
- const schema: any = {
188
- type: 'timeline' as const,
189
- objectName: 'events',
190
- startDateField: 'start_date',
191
- titleField: 'name',
192
- colorField: 'priority_color',
193
- };
194
- render(<ObjectTimeline schema={schema} data={mockData} />);
195
- const renderedSchema = mockTimelineRenderer.mock.calls[0][0].schema;
196
- expect(renderedSchema.items[0].color).toBe('red');
197
- });
198
-
199
- it('falls back to flat scale when timeline.scale is not set', () => {
200
- const schema: any = {
201
- type: 'timeline' as const,
202
- objectName: 'events',
203
- startDateField: 'start_date',
204
- titleField: 'name',
205
- scale: 'week',
206
- };
207
- render(<ObjectTimeline schema={schema} data={mockData} />);
208
- const renderedSchema = mockTimelineRenderer.mock.calls[0][0].schema;
209
- expect(renderedSchema.timeScale).toBe('week');
210
- });
211
-
212
- it('spec timeline config takes priority over flat props', () => {
213
- const schema: any = {
214
- type: 'timeline' as const,
215
- objectName: 'events',
216
- startDateField: 'end_date', // flat — should be overridden
217
- titleField: 'id', // flat — should be overridden
218
- groupByField: 'priority_color', // flat — should be overridden
219
- timeline: {
220
- startDateField: 'start_date', // spec — takes priority
221
- titleField: 'name', // spec — takes priority
222
- groupByField: 'category', // spec — takes priority
223
- },
224
- };
225
- render(<ObjectTimeline schema={schema} data={mockData} />);
226
- const renderedSchema = mockTimelineRenderer.mock.calls[0][0].schema;
227
- expect(renderedSchema.items[0].time).toBe('2024-01-01');
228
- expect(renderedSchema.items[0].title).toBe('Event A');
229
- expect(renderedSchema.items[0].group).toBe('work');
230
- });
231
- });
232
- });
@@ -1,295 +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 React from 'react';
10
- import { describe, it, expect, vi, beforeEach } from 'vitest';
11
- import { render, screen } from '@testing-library/react';
12
- import '@testing-library/jest-dom';
13
- import { ComponentRegistry } from '@object-ui/core';
14
-
15
- // Import renderer to trigger registration
16
- import '../renderer';
17
-
18
- // Mock renderChildren used by the renderer, preserving cn and other exports
19
- vi.mock('@object-ui/components', async (importOriginal) => {
20
- const actual = await importOriginal<typeof import('@object-ui/components')>();
21
- return {
22
- ...actual,
23
- renderChildren: vi.fn(() => null),
24
- };
25
- });
26
-
27
- describe('Timeline Renderer (ComponentRegistry)', () => {
28
- let TimelineComponent: any;
29
-
30
- beforeEach(() => {
31
- TimelineComponent = ComponentRegistry.get('timeline');
32
- });
33
-
34
- it('is registered in ComponentRegistry as "timeline"', () => {
35
- expect(TimelineComponent).toBeDefined();
36
- });
37
-
38
- // --- Vertical variant ---
39
-
40
- it('renders in vertical variant by default', () => {
41
- const schema: any = {
42
- type: 'timeline',
43
- items: [
44
- { time: '2024-01-15', title: 'First Event', description: 'Description one' },
45
- ],
46
- };
47
-
48
- const { container } = render(<TimelineComponent schema={schema} />);
49
- // Vertical uses an <ol> element
50
- expect(container.querySelector('ol')).toBeInTheDocument();
51
- expect(screen.getByText('First Event')).toBeInTheDocument();
52
- expect(screen.getByText('Description one')).toBeInTheDocument();
53
- });
54
-
55
- it('renders items with titles and descriptions in vertical variant', () => {
56
- const schema: any = {
57
- type: 'timeline',
58
- variant: 'vertical',
59
- items: [
60
- { time: '2024-01-15', title: 'Alpha Release', description: 'Initial alpha' },
61
- { time: '2024-02-01', title: 'Beta Release', description: 'Public beta' },
62
- { time: '2024-03-10', title: 'GA Launch', description: 'General availability' },
63
- ],
64
- };
65
-
66
- render(<TimelineComponent schema={schema} />);
67
- expect(screen.getByText('Alpha Release')).toBeInTheDocument();
68
- expect(screen.getByText('Initial alpha')).toBeInTheDocument();
69
- expect(screen.getByText('Beta Release')).toBeInTheDocument();
70
- expect(screen.getByText('Public beta')).toBeInTheDocument();
71
- expect(screen.getByText('GA Launch')).toBeInTheDocument();
72
- expect(screen.getByText('General availability')).toBeInTheDocument();
73
- });
74
-
75
- it('handles empty items array in vertical variant', () => {
76
- const schema: any = {
77
- type: 'timeline',
78
- variant: 'vertical',
79
- items: [],
80
- };
81
-
82
- const { container } = render(<TimelineComponent schema={schema} />);
83
- // Should render the <ol> wrapper but no <li> items
84
- expect(container.querySelector('ol')).toBeInTheDocument();
85
- expect(container.querySelectorAll('li')).toHaveLength(0);
86
- });
87
-
88
- it('formats dates correctly with short format', () => {
89
- const schema: any = {
90
- type: 'timeline',
91
- variant: 'vertical',
92
- dateFormat: 'short',
93
- items: [
94
- { time: '2024-06-15', title: 'Event' },
95
- ],
96
- };
97
-
98
- render(<TimelineComponent schema={schema} />);
99
- // short format uses toLocaleDateString()
100
- const expected = new Date('2024-06-15').toLocaleDateString();
101
- expect(screen.getByText(expected)).toBeInTheDocument();
102
- });
103
-
104
- it('formats dates correctly with long format', () => {
105
- const schema: any = {
106
- type: 'timeline',
107
- variant: 'vertical',
108
- dateFormat: 'long',
109
- items: [
110
- { time: '2024-06-15', title: 'Event' },
111
- ],
112
- };
113
-
114
- render(<TimelineComponent schema={schema} />);
115
- const expected = new Date('2024-06-15').toLocaleDateString('en-US', {
116
- year: 'numeric',
117
- month: 'long',
118
- day: 'numeric',
119
- });
120
- expect(screen.getByText(expected)).toBeInTheDocument();
121
- });
122
-
123
- it('formats dates correctly with iso (default) format', () => {
124
- const schema: any = {
125
- type: 'timeline',
126
- variant: 'vertical',
127
- dateFormat: 'iso',
128
- items: [
129
- { time: '2024-06-15', title: 'Event' },
130
- ],
131
- };
132
-
133
- render(<TimelineComponent schema={schema} />);
134
- expect(screen.getByText('2024-06-15')).toBeInTheDocument();
135
- });
136
-
137
- // --- Horizontal variant ---
138
-
139
- it('renders in horizontal variant', () => {
140
- const schema: any = {
141
- type: 'timeline',
142
- variant: 'horizontal',
143
- items: [
144
- { time: '2024-01-01', title: 'Q1', description: 'First quarter' },
145
- { time: '2024-04-01', title: 'Q2', description: 'Second quarter' },
146
- ],
147
- };
148
-
149
- render(<TimelineComponent schema={schema} />);
150
- expect(screen.getByText('Q1')).toBeInTheDocument();
151
- expect(screen.getByText('First quarter')).toBeInTheDocument();
152
- expect(screen.getByText('Q2')).toBeInTheDocument();
153
- expect(screen.getByText('Second quarter')).toBeInTheDocument();
154
- });
155
-
156
- it('handles empty items array in horizontal variant', () => {
157
- const schema: any = {
158
- type: 'timeline',
159
- variant: 'horizontal',
160
- items: [],
161
- };
162
-
163
- const { container } = render(<TimelineComponent schema={schema} />);
164
- // Horizontal wraps in a div, should have no child items
165
- expect(container.firstChild).toBeInTheDocument();
166
- expect(screen.queryByRole('heading')).not.toBeInTheDocument();
167
- });
168
-
169
- // --- Gantt variant ---
170
-
171
- it('renders in gantt variant', () => {
172
- const schema: any = {
173
- type: 'timeline',
174
- variant: 'gantt',
175
- timeScale: 'month',
176
- items: [
177
- {
178
- label: 'Backend',
179
- items: [
180
- { title: 'API Design', startDate: '2024-01-01', endDate: '2024-02-01' },
181
- ],
182
- },
183
- {
184
- label: 'Frontend',
185
- items: [
186
- { title: 'UI Build', startDate: '2024-02-01', endDate: '2024-03-01' },
187
- ],
188
- },
189
- ],
190
- };
191
-
192
- render(<TimelineComponent schema={schema} />);
193
- expect(screen.getByText('Backend')).toBeInTheDocument();
194
- expect(screen.getByText('Frontend')).toBeInTheDocument();
195
- expect(screen.getByText('API Design')).toBeInTheDocument();
196
- expect(screen.getByText('UI Build')).toBeInTheDocument();
197
- });
198
-
199
- it('handles rows with no sub-items in gantt variant', () => {
200
- const schema: any = {
201
- type: 'timeline',
202
- variant: 'gantt',
203
- minDate: '2024-01-01',
204
- maxDate: '2024-03-01',
205
- items: [
206
- { label: 'Empty Row', items: [] },
207
- {
208
- label: 'Has Items',
209
- items: [
210
- { title: 'Task', startDate: '2024-01-15', endDate: '2024-02-15' },
211
- ],
212
- },
213
- ],
214
- };
215
-
216
- render(<TimelineComponent schema={schema} />);
217
- expect(screen.getByText('Empty Row')).toBeInTheDocument();
218
- expect(screen.getByText('Has Items')).toBeInTheDocument();
219
- });
220
-
221
- it('gantt variant calculates date ranges from items', () => {
222
- const schema: any = {
223
- type: 'timeline',
224
- variant: 'gantt',
225
- timeScale: 'month',
226
- items: [
227
- {
228
- label: 'Project A',
229
- items: [
230
- { title: 'Task 1', startDate: '2024-01-01', endDate: '2024-01-31' },
231
- { title: 'Task 2', startDate: '2024-02-01', endDate: '2024-03-31' },
232
- ],
233
- },
234
- ],
235
- };
236
-
237
- render(<TimelineComponent schema={schema} />);
238
- // Gantt header should contain month labels spanning the range
239
- expect(screen.getByText('Project A')).toBeInTheDocument();
240
- expect(screen.getByText('Task 1')).toBeInTheDocument();
241
- expect(screen.getByText('Task 2')).toBeInTheDocument();
242
- });
243
-
244
- it('gantt variant uses custom rowLabel', () => {
245
- const schema: any = {
246
- type: 'timeline',
247
- variant: 'gantt',
248
- rowLabel: 'Projects',
249
- minDate: '2024-01-01',
250
- maxDate: '2024-03-01',
251
- items: [
252
- {
253
- label: 'My Project',
254
- items: [
255
- { title: 'Work', startDate: '2024-01-15', endDate: '2024-02-15' },
256
- ],
257
- },
258
- ],
259
- };
260
-
261
- render(<TimelineComponent schema={schema} />);
262
- expect(screen.getByText('Projects')).toBeInTheDocument();
263
- });
264
-
265
- it('gantt variant defaults rowLabel to "Items"', () => {
266
- const schema: any = {
267
- type: 'timeline',
268
- variant: 'gantt',
269
- minDate: '2024-01-01',
270
- maxDate: '2024-03-01',
271
- items: [
272
- {
273
- label: 'Row',
274
- items: [
275
- { title: 'Bar', startDate: '2024-01-10', endDate: '2024-02-10' },
276
- ],
277
- },
278
- ],
279
- };
280
-
281
- render(<TimelineComponent schema={schema} />);
282
- expect(screen.getByText('Items')).toBeInTheDocument();
283
- });
284
-
285
- it('returns null for unknown variant', () => {
286
- const schema: any = {
287
- type: 'timeline',
288
- variant: 'unknown',
289
- items: [],
290
- };
291
-
292
- const { container } = render(<TimelineComponent schema={schema} />);
293
- expect(container.innerHTML).toBe('');
294
- });
295
- });
@@ -1,150 +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 React from 'react';
10
- import { describe, it, expect } from 'vitest';
11
- import { render } from '@testing-library/react';
12
- import '@testing-library/jest-dom';
13
- import {
14
- Timeline,
15
- TimelineItem,
16
- TimelineMarker,
17
- timelineItemVariants,
18
- timelineMarkerVariants,
19
- } from '../index';
20
-
21
- describe('TimelineItem density variants', () => {
22
- it('renders with default density (mb-10)', () => {
23
- const { container } = render(
24
- <Timeline>
25
- <TimelineItem data-testid="item">Content</TimelineItem>
26
- </Timeline>
27
- );
28
- const item = container.querySelector('li');
29
- expect(item?.className).toContain('mb-10');
30
- expect(item?.className).toContain('ml-6');
31
- });
32
-
33
- it('renders with compact density (mb-3)', () => {
34
- const { container } = render(
35
- <Timeline>
36
- <TimelineItem density="compact">Content</TimelineItem>
37
- </Timeline>
38
- );
39
- const item = container.querySelector('li');
40
- expect(item?.className).toContain('mb-3');
41
- expect(item?.className).toContain('ml-6');
42
- });
43
-
44
- it('renders with comfortable density (mb-6)', () => {
45
- const { container } = render(
46
- <Timeline>
47
- <TimelineItem density="comfortable">Content</TimelineItem>
48
- </Timeline>
49
- );
50
- const item = container.querySelector('li');
51
- expect(item?.className).toContain('mb-6');
52
- expect(item?.className).toContain('ml-6');
53
- });
54
-
55
- it('allows className override', () => {
56
- const { container } = render(
57
- <Timeline>
58
- <TimelineItem density="compact" className="custom-class">Content</TimelineItem>
59
- </Timeline>
60
- );
61
- const item = container.querySelector('li');
62
- expect(item?.className).toContain('custom-class');
63
- expect(item?.className).toContain('mb-3');
64
- });
65
-
66
- it('cva function produces expected classes', () => {
67
- expect(timelineItemVariants({ density: 'default' })).toContain('mb-10');
68
- expect(timelineItemVariants({ density: 'compact' })).toContain('mb-3');
69
- expect(timelineItemVariants({ density: 'comfortable' })).toContain('mb-6');
70
- });
71
- });
72
-
73
- describe('TimelineMarker semantic color variants', () => {
74
- it('renders with default variant', () => {
75
- const { container } = render(<TimelineMarker />);
76
- const marker = container.firstChild as HTMLElement;
77
- expect(marker.className).toContain('bg-blue-200');
78
- expect(marker.className).toContain('border-blue-400');
79
- });
80
-
81
- it('renders with success variant', () => {
82
- const { container } = render(<TimelineMarker variant="success" />);
83
- const marker = container.firstChild as HTMLElement;
84
- expect(marker.className).toContain('bg-emerald-200');
85
- expect(marker.className).toContain('border-emerald-500');
86
- });
87
-
88
- it('renders with warning variant', () => {
89
- const { container } = render(<TimelineMarker variant="warning" />);
90
- const marker = container.firstChild as HTMLElement;
91
- expect(marker.className).toContain('bg-amber-200');
92
- expect(marker.className).toContain('border-amber-500');
93
- });
94
-
95
- it('renders with danger variant', () => {
96
- const { container } = render(<TimelineMarker variant="danger" />);
97
- const marker = container.firstChild as HTMLElement;
98
- expect(marker.className).toContain('bg-red-200');
99
- expect(marker.className).toContain('border-red-500');
100
- });
101
-
102
- it('renders with info variant', () => {
103
- const { container } = render(<TimelineMarker variant="info" />);
104
- const marker = container.firstChild as HTMLElement;
105
- expect(marker.className).toContain('bg-purple-200');
106
- expect(marker.className).toContain('border-purple-500');
107
- });
108
-
109
- it('renders with todo status variant', () => {
110
- const { container } = render(<TimelineMarker variant="todo" />);
111
- const marker = container.firstChild as HTMLElement;
112
- expect(marker.className).toContain('bg-slate-200');
113
- expect(marker.className).toContain('border-slate-400');
114
- });
115
-
116
- it('renders with in-progress status variant', () => {
117
- const { container } = render(<TimelineMarker variant="in-progress" />);
118
- const marker = container.firstChild as HTMLElement;
119
- expect(marker.className).toContain('bg-blue-200');
120
- expect(marker.className).toContain('border-blue-500');
121
- });
122
-
123
- it('renders with done status variant', () => {
124
- const { container } = render(<TimelineMarker variant="done" />);
125
- const marker = container.firstChild as HTMLElement;
126
- expect(marker.className).toContain('bg-emerald-200');
127
- expect(marker.className).toContain('border-emerald-500');
128
- });
129
-
130
- it('cva function produces expected classes for status variants', () => {
131
- expect(timelineMarkerVariants({ variant: 'todo' })).toContain('bg-slate-200');
132
- expect(timelineMarkerVariants({ variant: 'in-progress' })).toContain('bg-blue-200');
133
- expect(timelineMarkerVariants({ variant: 'done' })).toContain('bg-emerald-200');
134
- });
135
-
136
- it('applies common base classes', () => {
137
- const { container } = render(<TimelineMarker variant="default" />);
138
- const marker = container.firstChild as HTMLElement;
139
- expect(marker.className).toContain('rounded-full');
140
- expect(marker.className).toContain('border-2');
141
- expect(marker.className).toContain('absolute');
142
- });
143
-
144
- it('allows className override', () => {
145
- const { container } = render(<TimelineMarker variant="success" className="extra-class" />);
146
- const marker = container.firstChild as HTMLElement;
147
- expect(marker.className).toContain('extra-class');
148
- expect(marker.className).toContain('bg-emerald-200');
149
- });
150
- });