@object-ui/plugin-calendar 3.0.3 → 3.1.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.
@@ -22,12 +22,13 @@
22
22
  * - Works with object/api/value data providers
23
23
  */
24
24
 
25
- import React, { useEffect, useState, useCallback, useMemo } from 'react';
25
+ import React, { useEffect, useState, useCallback, useMemo, useRef } from 'react';
26
26
  import type { ObjectGridSchema, DataSource, ViewData, CalendarConfig } from '@object-ui/types';
27
27
  import { CalendarView, type CalendarEvent } from './CalendarView';
28
28
  import { usePullToRefresh } from '@object-ui/mobile';
29
29
  import { useNavigationOverlay } from '@object-ui/react';
30
30
  import { NavigationOverlay } from '@object-ui/components';
31
+ import { extractRecords, buildExpandFields } from '@object-ui/core';
31
32
 
32
33
  export interface CalendarSchema {
33
34
  type: 'calendar';
@@ -53,6 +54,8 @@ export interface ObjectCalendarProps {
53
54
  onDelete?: (record: any) => void;
54
55
  onNavigate?: (date: Date) => void;
55
56
  onViewChange?: (view: 'month' | 'week' | 'day') => void;
57
+ onEventDrop?: (record: any, newStart: Date, newEnd?: Date) => void;
58
+ locale?: string;
56
59
  }
57
60
 
58
61
  /**
@@ -144,6 +147,8 @@ export const ObjectCalendar: React.FC<ObjectCalendarProps> = ({
144
147
  onDateClick,
145
148
  onNavigate,
146
149
  onViewChange,
150
+ onEventDrop,
151
+ locale,
147
152
  ...rest
148
153
  }) => {
149
154
  const [data, setData] = useState<any[]>([]);
@@ -178,6 +183,10 @@ export const ObjectCalendar: React.FC<ObjectCalendarProps> = ({
178
183
  ]);
179
184
  const hasInlineData = dataConfig?.provider === 'value';
180
185
 
186
+ // Use ref for objectSchema to avoid double-fetch on mount
187
+ const objectSchemaRef = useRef<any>(null);
188
+ objectSchemaRef.current = objectSchema;
189
+
181
190
  // Fetch data based on provider
182
191
  useEffect(() => {
183
192
  let isMounted = true;
@@ -204,28 +213,21 @@ export const ObjectCalendar: React.FC<ObjectCalendarProps> = ({
204
213
  }
205
214
  }
206
215
 
207
- if (!dataSource) {
216
+ if (!dataSource || typeof dataSource.find !== 'function') {
208
217
  throw new Error('DataSource required for object/api providers');
209
218
  }
210
219
 
211
220
  if (dataConfig?.provider === 'object') {
212
221
  const objectName = dataConfig.object;
222
+ // Auto-inject $expand for lookup/master_detail fields
223
+ const expand = buildExpandFields(objectSchemaRef.current?.fields);
213
224
  const result = await dataSource.find(objectName, {
214
225
  $filter: schema.filter,
215
226
  $orderby: convertSortToQueryParams(schema.sort),
227
+ ...(expand.length > 0 ? { $expand: expand } : {}),
216
228
  });
217
229
 
218
- let items: any[] = [];
219
-
220
- if (Array.isArray(result)) {
221
- items = result;
222
- } else if (result && typeof result === 'object') {
223
- if (Array.isArray((result as any).data)) {
224
- items = (result as any).data;
225
- } else if (Array.isArray((result as any).value)) {
226
- items = (result as any).value;
227
- }
228
- }
230
+ let items: any[] = extractRecords(result);
229
231
 
230
232
  if (isMounted) {
231
233
  setData(items);
@@ -362,6 +364,7 @@ export const ObjectCalendar: React.FC<ObjectCalendarProps> = ({
362
364
  events={events}
363
365
  currentDate={currentDate}
364
366
  view={(schema as any).defaultView || 'month'}
367
+ locale={locale}
365
368
  onEventClick={(event) => {
366
369
  navigation.handleClick(event.data);
367
370
  onEventClick?.(event.data);
@@ -375,7 +378,10 @@ export const ObjectCalendar: React.FC<ObjectCalendarProps> = ({
375
378
  setView(v);
376
379
  onViewChange?.(v);
377
380
  }}
378
- onAddClick={handleCreate}
381
+ onAddClick={undefined}
382
+ onEventDrop={onEventDrop ? (event, newStart, newEnd) => {
383
+ onEventDrop(event.data, newStart, newEnd);
384
+ } : undefined}
379
385
  />
380
386
  </div>
381
387
  {navigation.isOverlay && (
@@ -276,7 +276,7 @@ describe('CalendarView: Screen Reader & Accessibility', () => {
276
276
  />
277
277
  );
278
278
 
279
- const newButton = screen.getByText('New');
279
+ const newButton = screen.getByText('New event');
280
280
  expect(newButton).toBeInTheDocument();
281
281
  expect(newButton.closest('button')).toBeInTheDocument();
282
282
  });
@@ -284,7 +284,7 @@ describe('CalendarView: Screen Reader & Accessibility', () => {
284
284
  it('add button is not shown when onAddClick is absent', () => {
285
285
  render(<CalendarView currentDate={defaultDate} locale="en-US" />);
286
286
 
287
- expect(screen.queryByText('New')).not.toBeInTheDocument();
287
+ expect(screen.queryByText('New event')).not.toBeInTheDocument();
288
288
  });
289
289
  });
290
290
  });
@@ -0,0 +1,230 @@
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
+ /**
10
+ * Tests for calendar bug fixes:
11
+ * - Event click always dispatches action (no schema.onEventClick guard)
12
+ * - i18n integration with useObjectTranslation
13
+ * - Tooltip (title attribute) on truncated event titles
14
+ * - Cross-month date visual distinction (opacity)
15
+ * - Today highlight spacing improvement
16
+ */
17
+
18
+ import { describe, it, expect, vi } from 'vitest';
19
+ import { render, screen, fireEvent } from '@testing-library/react';
20
+ import '@testing-library/jest-dom';
21
+ import React from 'react';
22
+ import { CalendarView, type CalendarEvent } from '../CalendarView';
23
+ import { I18nProvider } from '@object-ui/i18n';
24
+
25
+ // Mock ResizeObserver
26
+ class ResizeObserver {
27
+ observe() {}
28
+ unobserve() {}
29
+ disconnect() {}
30
+ }
31
+ global.ResizeObserver = ResizeObserver;
32
+
33
+ // Mock PointerEvents for Radix
34
+ if (!global.PointerEvent) {
35
+ class PointerEvent extends Event {
36
+ button: number;
37
+ ctrlKey: boolean;
38
+ metaKey: boolean;
39
+ shiftKey: boolean;
40
+ constructor(type: string, props: any = {}) {
41
+ super(type, props);
42
+ this.button = props.button || 0;
43
+ this.ctrlKey = props.ctrlKey || false;
44
+ this.metaKey = props.metaKey || false;
45
+ this.shiftKey = props.shiftKey || false;
46
+ }
47
+ }
48
+ // @ts-expect-error Mocking global PointerEvent
49
+ global.PointerEvent = PointerEvent as any;
50
+ }
51
+
52
+ // Mock HTMLElement.offsetParent for Radix Popper
53
+ Object.defineProperty(HTMLElement.prototype, 'offsetParent', {
54
+ get() {
55
+ return this.parentElement;
56
+ },
57
+ });
58
+
59
+ const defaultDate = new Date(2024, 0, 15); // Jan 15, 2024
60
+
61
+ const sampleEvents: CalendarEvent[] = [
62
+ {
63
+ id: '1',
64
+ title: 'Design dashboard widget layout and structure',
65
+ start: new Date(2024, 0, 15, 10, 0),
66
+ end: new Date(2024, 0, 15, 11, 0),
67
+ color: '#3b82f6',
68
+ },
69
+ {
70
+ id: '2',
71
+ title: 'Short Event',
72
+ start: new Date(2024, 0, 15, 14, 0),
73
+ },
74
+ ];
75
+
76
+ function renderWithI18n(ui: React.ReactElement, lang = 'en') {
77
+ return render(
78
+ <I18nProvider config={{ defaultLanguage: lang, detectBrowserLanguage: false }}>
79
+ {ui}
80
+ </I18nProvider>
81
+ );
82
+ }
83
+
84
+ describe('Calendar Bug Fixes', () => {
85
+ describe('event click dispatches action', () => {
86
+ it('fires onEventClick when event is clicked in month view', () => {
87
+ const onClick = vi.fn();
88
+ renderWithI18n(
89
+ <CalendarView
90
+ currentDate={defaultDate}
91
+ events={sampleEvents}
92
+ view="month"
93
+ onEventClick={onClick}
94
+ />
95
+ );
96
+ fireEvent.click(screen.getByText('Short Event'));
97
+ expect(onClick).toHaveBeenCalledTimes(1);
98
+ expect(onClick).toHaveBeenCalledWith(sampleEvents[1]);
99
+ });
100
+ });
101
+
102
+ describe('tooltip on truncated event titles', () => {
103
+ it('renders title attribute on event cards in month view', () => {
104
+ renderWithI18n(
105
+ <CalendarView
106
+ currentDate={defaultDate}
107
+ events={sampleEvents}
108
+ view="month"
109
+ />
110
+ );
111
+ const eventEl = screen.getByText('Short Event');
112
+ expect(eventEl.closest('[title]')).toHaveAttribute('title', 'Short Event');
113
+ });
114
+
115
+ it('renders title attribute with long title in month view', () => {
116
+ renderWithI18n(
117
+ <CalendarView
118
+ currentDate={defaultDate}
119
+ events={sampleEvents}
120
+ view="month"
121
+ />
122
+ );
123
+ const eventEl = screen.getByText('Design dashboard widget layout and structure');
124
+ expect(eventEl.closest('[title]')).toHaveAttribute(
125
+ 'title',
126
+ 'Design dashboard widget layout and structure'
127
+ );
128
+ });
129
+
130
+ it('renders title attribute on event cards in day view', () => {
131
+ renderWithI18n(
132
+ <CalendarView
133
+ currentDate={defaultDate}
134
+ events={sampleEvents}
135
+ view="day"
136
+ />
137
+ );
138
+ const eventEl = screen.getByText('Short Event');
139
+ expect(eventEl.closest('[title]')).toHaveAttribute('title', 'Short Event');
140
+ });
141
+ });
142
+
143
+ describe('i18n integration', () => {
144
+ it('renders labels from i18n when language is en', () => {
145
+ const onAdd = vi.fn();
146
+ renderWithI18n(
147
+ <CalendarView
148
+ currentDate={defaultDate}
149
+ events={[]}
150
+ onAddClick={onAdd}
151
+ />,
152
+ 'en'
153
+ );
154
+ expect(screen.getByText('Today')).toBeInTheDocument();
155
+ expect(screen.getByText('New event')).toBeInTheDocument();
156
+ });
157
+
158
+ it('renders labels from i18n when language is zh', () => {
159
+ const onAdd = vi.fn();
160
+ renderWithI18n(
161
+ <CalendarView
162
+ currentDate={defaultDate}
163
+ events={[]}
164
+ onAddClick={onAdd}
165
+ />,
166
+ 'zh'
167
+ );
168
+ expect(screen.getByText('今天')).toBeInTheDocument();
169
+ expect(screen.getByText('新建事件')).toBeInTheDocument();
170
+ });
171
+
172
+ it('renders locale-aware weekday headers for zh', () => {
173
+ renderWithI18n(
174
+ <CalendarView
175
+ currentDate={defaultDate}
176
+ events={[]}
177
+ view="month"
178
+ />,
179
+ 'zh'
180
+ );
181
+ expect(screen.getByText('周日')).toBeInTheDocument();
182
+ expect(screen.getByText('周一')).toBeInTheDocument();
183
+ });
184
+ });
185
+
186
+ describe('cross-month date styling', () => {
187
+ it('applies opacity to non-current-month dates', () => {
188
+ const { container } = renderWithI18n(
189
+ <CalendarView
190
+ currentDate={new Date(2024, 1, 15)} // Feb 15, 2024
191
+ events={[]}
192
+ view="month"
193
+ />
194
+ );
195
+ const gridCells = container.querySelectorAll('[role="gridcell"]');
196
+ const firstCell = gridCells[0];
197
+ expect(firstCell.className).toContain('opacity-50');
198
+ });
199
+
200
+ it('does not apply opacity to current-month dates', () => {
201
+ const { container } = renderWithI18n(
202
+ <CalendarView
203
+ currentDate={new Date(2024, 1, 15)} // Feb 15, 2024
204
+ events={[]}
205
+ view="month"
206
+ />
207
+ );
208
+ const gridCells = container.querySelectorAll('[role="gridcell"]');
209
+ // Feb 2024 starts on Thursday, so index 4 (0-based) is Feb 1
210
+ const feb1Cell = gridCells[4];
211
+ expect(feb1Cell.className).not.toContain('opacity-50');
212
+ });
213
+ });
214
+
215
+ describe('today highlight spacing', () => {
216
+ it('renders today date with mb-2 spacing for better separation', () => {
217
+ const today = new Date();
218
+ const { container } = renderWithI18n(
219
+ <CalendarView
220
+ currentDate={today}
221
+ events={[]}
222
+ view="month"
223
+ />
224
+ );
225
+ const todayEl = container.querySelector('[aria-current="date"]');
226
+ expect(todayEl).toBeInTheDocument();
227
+ expect(todayEl?.className).toContain('mb-2');
228
+ });
229
+ });
230
+ });
@@ -0,0 +1,178 @@
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
+ * Tests for CalendarView optimizations:
9
+ * - Event index (Map-based lookup instead of O(N) per cell)
10
+ * - Stable default date reference
11
+ * - HEX color text-white fix
12
+ * - onEventDrop / locale passthrough from ObjectCalendar
13
+ */
14
+
15
+ import { describe, it, expect, vi } from 'vitest';
16
+ import { render, screen } from '@testing-library/react';
17
+ import '@testing-library/jest-dom';
18
+ import React from 'react';
19
+ import { CalendarView, type CalendarEvent } from '../CalendarView';
20
+
21
+ // Mock @object-ui/components
22
+ vi.mock('@object-ui/components', () => ({
23
+ cn: (...classes: (string | undefined | boolean)[]) => classes.filter(Boolean).join(' '),
24
+ Button: ({ children, ...props }: any) => <button {...props}>{children}</button>,
25
+ Select: ({ children, value, onValueChange }: any) => <div data-testid="select">{children}</div>,
26
+ SelectContent: ({ children }: any) => <div>{children}</div>,
27
+ SelectItem: ({ children, value }: any) => <div data-value={value}>{children}</div>,
28
+ SelectTrigger: ({ children }: any) => <div>{children}</div>,
29
+ SelectValue: () => <span>Month</span>,
30
+ Calendar: (props: any) => <div data-testid="calendar-picker">Calendar Picker</div>,
31
+ Popover: ({ children }: any) => <div>{children}</div>,
32
+ PopoverContent: ({ children }: any) => <div>{children}</div>,
33
+ PopoverTrigger: ({ children }: any) => <div>{children}</div>,
34
+ }));
35
+
36
+ // Mock lucide-react icons
37
+ vi.mock('lucide-react', () => ({
38
+ ChevronLeftIcon: (props: any) => <svg data-testid="chevron-left" {...props} />,
39
+ ChevronRightIcon: (props: any) => <svg data-testid="chevron-right" {...props} />,
40
+ CalendarIcon: (props: any) => <svg data-testid="calendar-icon" {...props} />,
41
+ PlusIcon: (props: any) => <svg data-testid="plus-icon" {...props} />,
42
+ }));
43
+
44
+ // Mock ResizeObserver
45
+ class ResizeObserver {
46
+ observe() {}
47
+ unobserve() {}
48
+ disconnect() {}
49
+ }
50
+ global.ResizeObserver = ResizeObserver;
51
+
52
+ const baseDate = new Date(2024, 0, 15); // Jan 15, 2024
53
+
54
+ describe('CalendarView optimizations', () => {
55
+ describe('event index (Map-based lookup)', () => {
56
+ it('renders single-day events correctly with the new index', () => {
57
+ const events: CalendarEvent[] = [
58
+ { id: '1', title: 'Morning Meeting', start: new Date(2024, 0, 15, 9, 0), end: new Date(2024, 0, 15, 10, 0) },
59
+ { id: '2', title: 'Lunch', start: new Date(2024, 0, 15, 12, 0), end: new Date(2024, 0, 15, 13, 0) },
60
+ ];
61
+ render(<CalendarView currentDate={baseDate} events={events} view="month" locale="en-US" />);
62
+ expect(screen.getByText('Morning Meeting')).toBeInTheDocument();
63
+ expect(screen.getByText('Lunch')).toBeInTheDocument();
64
+ });
65
+
66
+ it('renders multi-day events on all spanned days', () => {
67
+ const events: CalendarEvent[] = [
68
+ {
69
+ id: 'multi-1',
70
+ title: 'Conference',
71
+ start: new Date(2024, 0, 15, 9, 0),
72
+ end: new Date(2024, 0, 17, 17, 0),
73
+ allDay: true,
74
+ },
75
+ ];
76
+ const { container } = render(
77
+ <CalendarView currentDate={baseDate} events={events} view="month" locale="en-US" />
78
+ );
79
+ // The multi-day event should appear on Jan 15, 16, and 17 — 3 cells
80
+ const eventElements = container.querySelectorAll('[role="button"][aria-label="Conference"]');
81
+ expect(eventElements.length).toBe(3);
82
+ });
83
+
84
+ it('renders events with no end date on their start day only', () => {
85
+ const events: CalendarEvent[] = [
86
+ { id: 'no-end', title: 'No End Event', start: new Date(2024, 0, 20, 14, 0) },
87
+ ];
88
+ const { container } = render(
89
+ <CalendarView currentDate={baseDate} events={events} view="month" locale="en-US" />
90
+ );
91
+ const eventElements = container.querySelectorAll('[role="button"][aria-label="No End Event"]');
92
+ expect(eventElements.length).toBe(1);
93
+ });
94
+ });
95
+
96
+ describe('HEX color text-white fix', () => {
97
+ it('applies text-white class when event color is a HEX value in month view', () => {
98
+ const events: CalendarEvent[] = [
99
+ { id: 'hex-1', title: 'HEX Event', start: new Date(2024, 0, 15), color: '#3b82f6' },
100
+ ];
101
+ const { container } = render(
102
+ <CalendarView currentDate={baseDate} events={events} view="month" locale="en-US" />
103
+ );
104
+ const eventEl = container.querySelector('[aria-label="HEX Event"]');
105
+ expect(eventEl).toBeInTheDocument();
106
+ expect(eventEl!.className).toContain('text-white');
107
+ expect((eventEl as HTMLElement).style.backgroundColor).toBe('#3b82f6');
108
+ });
109
+
110
+ it('uses default color class when event color is a Tailwind class', () => {
111
+ const events: CalendarEvent[] = [
112
+ { id: 'tw-1', title: 'Tailwind Event', start: new Date(2024, 0, 15), color: 'bg-red-500 text-white' },
113
+ ];
114
+ const { container } = render(
115
+ <CalendarView currentDate={baseDate} events={events} view="month" locale="en-US" />
116
+ );
117
+ const eventEl = container.querySelector('[aria-label="Tailwind Event"]');
118
+ expect(eventEl).toBeInTheDocument();
119
+ expect(eventEl!.className).toContain('bg-red-500');
120
+ expect(eventEl!.className).toContain('text-white');
121
+ });
122
+
123
+ it('falls back to DEFAULT_EVENT_COLOR when no color is specified', () => {
124
+ const events: CalendarEvent[] = [
125
+ { id: 'no-color', title: 'No Color', start: new Date(2024, 0, 15) },
126
+ ];
127
+ const { container } = render(
128
+ <CalendarView currentDate={baseDate} events={events} view="month" locale="en-US" />
129
+ );
130
+ const eventEl = container.querySelector('[aria-label="No Color"]');
131
+ expect(eventEl).toBeInTheDocument();
132
+ expect(eventEl!.className).toContain('bg-blue-500');
133
+ expect(eventEl!.className).toContain('text-white');
134
+ });
135
+ });
136
+
137
+ describe('stable default date', () => {
138
+ it('renders without currentDate prop without errors', () => {
139
+ const { container } = render(<CalendarView events={[]} locale="en-US" />);
140
+ expect(container).toBeTruthy();
141
+ });
142
+
143
+ it('does not trigger re-render loop when currentDate is not provided', () => {
144
+ const onNavigate = vi.fn();
145
+ const { rerender } = render(<CalendarView events={[]} locale="en-US" onNavigate={onNavigate} />);
146
+ // Re-render the same component — should NOT trigger onNavigate from the effect
147
+ rerender(<CalendarView events={[]} locale="en-US" onNavigate={onNavigate} />);
148
+ expect(onNavigate).not.toHaveBeenCalled();
149
+ });
150
+ });
151
+
152
+ describe('drag-and-drop enablement', () => {
153
+ it('events are draggable when onEventDrop is provided', () => {
154
+ const events: CalendarEvent[] = [
155
+ { id: 'd-1', title: 'Drag Event', start: new Date(2024, 0, 15) },
156
+ ];
157
+ const onEventDrop = vi.fn();
158
+ const { container } = render(
159
+ <CalendarView currentDate={baseDate} events={events} view="month" onEventDrop={onEventDrop} locale="en-US" />
160
+ );
161
+ const eventEl = container.querySelector('[aria-label="Drag Event"]');
162
+ expect(eventEl).toBeInTheDocument();
163
+ expect(eventEl!.getAttribute('draggable')).toBe('true');
164
+ });
165
+
166
+ it('events are not draggable when onEventDrop is not provided', () => {
167
+ const events: CalendarEvent[] = [
168
+ { id: 'nd-1', title: 'No Drag Event', start: new Date(2024, 0, 15) },
169
+ ];
170
+ const { container } = render(
171
+ <CalendarView currentDate={baseDate} events={events} view="month" locale="en-US" />
172
+ );
173
+ const eventEl = container.querySelector('[aria-label="No Drag Event"]');
174
+ expect(eventEl).toBeInTheDocument();
175
+ expect(eventEl!.getAttribute('draggable')).toBe('false');
176
+ });
177
+ });
178
+ });
@@ -265,8 +265,8 @@ describe('P3.3 Calendar View States', () => {
265
265
  onAddClick={onAdd}
266
266
  />
267
267
  );
268
- // The "New" button should be visible
269
- const newButton = screen.getByText('New');
268
+ // The "New event" button should be visible
269
+ const newButton = screen.getByText('New event');
270
270
  expect(newButton).toBeInTheDocument();
271
271
  fireEvent.click(newButton);
272
272
  expect(onAdd).toHaveBeenCalledTimes(1);
@@ -280,7 +280,7 @@ describe('P3.3 Calendar View States', () => {
280
280
  locale="en-US"
281
281
  />
282
282
  );
283
- expect(screen.queryByText('New')).not.toBeInTheDocument();
283
+ expect(screen.queryByText('New event')).not.toBeInTheDocument();
284
284
  });
285
285
 
286
286
  it('accepts className prop', () => {
@@ -43,15 +43,10 @@ ComponentRegistry.register('calendar-view',
43
43
  }, [schema.data, schema.titleField, schema.startDateField, schema.endDateField, schema.colorField, schema.allDayField]);
44
44
 
45
45
  const handleEventClick = (event: CalendarEvent) => {
46
- if (schema.onEventClick) {
47
- // Dispatch configured action
48
- // This would use the action runner in a real implementation
49
- // For now we just call onAction if provided
50
- onAction?.({
51
- type: 'event-click',
52
- payload: event
53
- });
54
- }
46
+ onAction?.({
47
+ type: 'event-click',
48
+ payload: event
49
+ });
55
50
  };
56
51
 
57
52
  const handleAddClick = () => {
@@ -67,7 +62,6 @@ ComponentRegistry.register('calendar-view',
67
62
  className={className}
68
63
  events={events}
69
64
  onEventClick={handleEventClick}
70
- onAddClick={handleAddClick}
71
65
  // Pass validation or other props
72
66
  {...props}
73
67
  />
package/src/index.tsx CHANGED
@@ -24,9 +24,9 @@ export type { CalendarViewProps, CalendarEvent } from './CalendarView';
24
24
  import './calendar-view-renderer';
25
25
 
26
26
  // Register object-calendar component
27
- export const ObjectCalendarRenderer: React.FC<{ schema: any }> = ({ schema }) => {
28
- const { dataSource } = useSchemaContext();
29
- return <ObjectCalendar schema={schema} dataSource={dataSource} />;
27
+ export const ObjectCalendarRenderer: React.FC<{ schema: any; [key: string]: any }> = ({ schema, data: _data, loading: _loading, ...props }) => {
28
+ const { dataSource } = useSchemaContext() || {};
29
+ return <ObjectCalendar schema={schema} dataSource={dataSource} {...props} />;
30
30
  };
31
31
 
32
32
  ComponentRegistry.register('object-calendar', ObjectCalendarRenderer, {