@object-ui/plugin-form 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 +31 -0
- package/README.md +21 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +116 -73
- package/dist/index.umd.cjs +2 -2
- package/dist/{plugin-form → packages/plugin-form}/src/DrawerForm.d.ts +2 -0
- package/dist/{plugin-form → packages/plugin-form}/src/autoLayout.d.ts +11 -4
- package/package.json +43 -11
- package/.turbo/turbo-build.log +0 -32
- package/src/DrawerForm.tsx +0 -410
- package/src/EmbeddableForm.tsx +0 -240
- package/src/FormAnalytics.tsx +0 -209
- package/src/FormSection.tsx +0 -152
- package/src/FormVariants.test.tsx +0 -219
- package/src/ModalForm.tsx +0 -485
- package/src/ObjectForm.msw.test.tsx +0 -156
- package/src/ObjectForm.stories.tsx +0 -85
- package/src/ObjectForm.test.tsx +0 -61
- package/src/ObjectForm.tsx +0 -609
- package/src/SplitForm.tsx +0 -300
- package/src/TabbedForm.tsx +0 -395
- package/src/WizardForm.tsx +0 -502
- package/src/__tests__/EmbeddableFormPrefill.test.tsx +0 -186
- package/src/__tests__/MobileUX.test.tsx +0 -433
- package/src/__tests__/NewVariants.test.tsx +0 -684
- package/src/__tests__/autoLayout.test.ts +0 -339
- package/src/__tests__/form-validation-submit.test.tsx +0 -286
- package/src/autoLayout.ts +0 -166
- package/src/index.tsx +0 -134
- package/tsconfig.json +0 -9
- package/vite.config.ts +0 -57
- package/vitest.config.ts +0 -12
- package/vitest.setup.ts +0 -1
- /package/dist/{plugin-form → packages/plugin-form}/src/EmbeddableForm.d.ts +0 -0
- /package/dist/{plugin-form → packages/plugin-form}/src/FormAnalytics.d.ts +0 -0
- /package/dist/{plugin-form → packages/plugin-form}/src/FormSection.d.ts +0 -0
- /package/dist/{plugin-form → packages/plugin-form}/src/ModalForm.d.ts +0 -0
- /package/dist/{plugin-form → packages/plugin-form}/src/ObjectForm.d.ts +0 -0
- /package/dist/{plugin-form → packages/plugin-form}/src/SplitForm.d.ts +0 -0
- /package/dist/{plugin-form → packages/plugin-form}/src/TabbedForm.d.ts +0 -0
- /package/dist/{plugin-form → packages/plugin-form}/src/WizardForm.d.ts +0 -0
- /package/dist/{plugin-form → packages/plugin-form}/src/index.d.ts +0 -0
|
@@ -1,433 +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
|
-
/**
|
|
10
|
-
* Mobile UX Tests for ModalForm
|
|
11
|
-
*
|
|
12
|
-
* Validates mobile-specific optimizations:
|
|
13
|
-
* - Skeleton loading state (replaces spinner)
|
|
14
|
-
* - Flex layout structure with sticky header/footer
|
|
15
|
-
* - Full-screen modal on mobile (h-[100dvh])
|
|
16
|
-
* - Close button touch target (min 44×44px)
|
|
17
|
-
* - Sticky footer with action buttons outside scroll area
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
21
|
-
import { render, screen, waitFor } from '@testing-library/react';
|
|
22
|
-
import React from 'react';
|
|
23
|
-
import { ModalForm } from '../ModalForm';
|
|
24
|
-
|
|
25
|
-
const mockObjectSchema = {
|
|
26
|
-
name: 'events',
|
|
27
|
-
fields: {
|
|
28
|
-
subject: { label: 'Subject', type: 'text', required: true },
|
|
29
|
-
start: { label: 'Start', type: 'datetime', required: true },
|
|
30
|
-
end: { label: 'End', type: 'datetime', required: true },
|
|
31
|
-
location: { label: 'Location', type: 'text', required: false },
|
|
32
|
-
description: { label: 'Description', type: 'textarea', required: false },
|
|
33
|
-
participants: { label: 'Participants', type: 'lookup', required: false },
|
|
34
|
-
type: { label: 'Type', type: 'select', required: false, options: [{ value: 'meeting', label: 'Meeting' }] },
|
|
35
|
-
},
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
const createMockDataSource = () => ({
|
|
39
|
-
getObjectSchema: vi.fn().mockResolvedValue(mockObjectSchema),
|
|
40
|
-
findOne: vi.fn().mockResolvedValue({ id: '1', subject: 'Test Event' }),
|
|
41
|
-
find: vi.fn().mockResolvedValue([]),
|
|
42
|
-
create: vi.fn().mockResolvedValue({ id: '1' }),
|
|
43
|
-
update: vi.fn().mockResolvedValue({ id: '1' }),
|
|
44
|
-
delete: vi.fn().mockResolvedValue(true),
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
describe('ModalForm Mobile UX', () => {
|
|
48
|
-
it('renders skeleton loading instead of spinner', () => {
|
|
49
|
-
const mockDataSource = createMockDataSource();
|
|
50
|
-
// Make getObjectSchema hang (never resolve) to keep loading state
|
|
51
|
-
mockDataSource.getObjectSchema.mockReturnValue(new Promise(() => {}));
|
|
52
|
-
|
|
53
|
-
render(
|
|
54
|
-
<ModalForm
|
|
55
|
-
schema={{
|
|
56
|
-
type: 'object-form',
|
|
57
|
-
formType: 'modal',
|
|
58
|
-
objectName: 'events',
|
|
59
|
-
mode: 'create',
|
|
60
|
-
title: 'Create Event',
|
|
61
|
-
open: true,
|
|
62
|
-
}}
|
|
63
|
-
dataSource={mockDataSource as any}
|
|
64
|
-
/>
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
// Should show skeleton loading, not the old spinner text
|
|
68
|
-
expect(screen.queryByText('Loading form...')).not.toBeInTheDocument();
|
|
69
|
-
expect(screen.getByTestId('modal-form-skeleton')).toBeInTheDocument();
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('renders with full-screen mobile classes and flex layout on MobileDialogContent', async () => {
|
|
73
|
-
const mockDataSource = createMockDataSource();
|
|
74
|
-
|
|
75
|
-
render(
|
|
76
|
-
<ModalForm
|
|
77
|
-
schema={{
|
|
78
|
-
type: 'object-form',
|
|
79
|
-
formType: 'modal',
|
|
80
|
-
objectName: 'events',
|
|
81
|
-
mode: 'create',
|
|
82
|
-
title: 'Create Event',
|
|
83
|
-
description: 'Add a new Event to your database.',
|
|
84
|
-
open: true,
|
|
85
|
-
}}
|
|
86
|
-
dataSource={mockDataSource as any}
|
|
87
|
-
/>
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
await waitFor(() => {
|
|
91
|
-
expect(screen.getByText('Create Event')).toBeInTheDocument();
|
|
92
|
-
});
|
|
93
|
-
expect(screen.getByText('Add a new Event to your database.')).toBeInTheDocument();
|
|
94
|
-
|
|
95
|
-
// MobileDialogContent should be rendered via portal
|
|
96
|
-
const dialogContent = document.querySelector('[role="dialog"]');
|
|
97
|
-
expect(dialogContent).not.toBeNull();
|
|
98
|
-
const cls = dialogContent!.className;
|
|
99
|
-
// Mobile full-screen
|
|
100
|
-
expect(cls).toContain('h-[100dvh]');
|
|
101
|
-
// Flex column layout for sticky header/footer
|
|
102
|
-
expect(cls).toContain('flex');
|
|
103
|
-
expect(cls).toContain('flex-col');
|
|
104
|
-
// Overflow hidden (scroll is on the body area)
|
|
105
|
-
expect(cls).toContain('overflow-hidden');
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it('close button has accessible touch target (≥44×44px on mobile)', async () => {
|
|
109
|
-
const mockDataSource = createMockDataSource();
|
|
110
|
-
|
|
111
|
-
render(
|
|
112
|
-
<ModalForm
|
|
113
|
-
schema={{
|
|
114
|
-
type: 'object-form',
|
|
115
|
-
formType: 'modal',
|
|
116
|
-
objectName: 'events',
|
|
117
|
-
mode: 'create',
|
|
118
|
-
title: 'Create Event',
|
|
119
|
-
open: true,
|
|
120
|
-
}}
|
|
121
|
-
dataSource={mockDataSource as any}
|
|
122
|
-
/>
|
|
123
|
-
);
|
|
124
|
-
|
|
125
|
-
await waitFor(() => {
|
|
126
|
-
expect(screen.getByText('Create Event')).toBeInTheDocument();
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
// Close button should have WCAG-compliant touch target classes
|
|
130
|
-
const closeButton = screen.getByRole('button', { name: /close/i });
|
|
131
|
-
expect(closeButton).toBeInTheDocument();
|
|
132
|
-
const cls = closeButton.className;
|
|
133
|
-
expect(cls).toContain('min-h-[44px]');
|
|
134
|
-
expect(cls).toContain('min-w-[44px]');
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
it('renders sticky footer with action buttons outside scroll area', async () => {
|
|
138
|
-
const mockDataSource = createMockDataSource();
|
|
139
|
-
|
|
140
|
-
render(
|
|
141
|
-
<ModalForm
|
|
142
|
-
schema={{
|
|
143
|
-
type: 'object-form',
|
|
144
|
-
formType: 'modal',
|
|
145
|
-
objectName: 'events',
|
|
146
|
-
mode: 'create',
|
|
147
|
-
title: 'Create Event',
|
|
148
|
-
open: true,
|
|
149
|
-
showSubmit: true,
|
|
150
|
-
showCancel: true,
|
|
151
|
-
submitText: 'Save Record',
|
|
152
|
-
cancelText: 'Cancel',
|
|
153
|
-
}}
|
|
154
|
-
dataSource={mockDataSource as any}
|
|
155
|
-
/>
|
|
156
|
-
);
|
|
157
|
-
|
|
158
|
-
// Footer should exist as a sibling to the scroll area, not inside it
|
|
159
|
-
const footer = await screen.findByTestId('modal-form-footer');
|
|
160
|
-
expect(footer.className).toContain('border-t');
|
|
161
|
-
expect(footer.className).toContain('shrink-0');
|
|
162
|
-
|
|
163
|
-
// Action buttons should be in the footer
|
|
164
|
-
const saveButton = screen.getByRole('button', { name: /save record/i });
|
|
165
|
-
const cancelButton = screen.getByRole('button', { name: /cancel/i });
|
|
166
|
-
expect(footer.contains(saveButton)).toBe(true);
|
|
167
|
-
expect(footer.contains(cancelButton)).toBe(true);
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
it('does not show footer during loading or error states', () => {
|
|
171
|
-
const mockDataSource = createMockDataSource();
|
|
172
|
-
mockDataSource.getObjectSchema.mockReturnValue(new Promise(() => {}));
|
|
173
|
-
|
|
174
|
-
render(
|
|
175
|
-
<ModalForm
|
|
176
|
-
schema={{
|
|
177
|
-
type: 'object-form',
|
|
178
|
-
formType: 'modal',
|
|
179
|
-
objectName: 'events',
|
|
180
|
-
mode: 'create',
|
|
181
|
-
title: 'Create Event',
|
|
182
|
-
open: true,
|
|
183
|
-
showSubmit: true,
|
|
184
|
-
showCancel: true,
|
|
185
|
-
}}
|
|
186
|
-
dataSource={mockDataSource as any}
|
|
187
|
-
/>
|
|
188
|
-
);
|
|
189
|
-
|
|
190
|
-
// While loading, footer should not be rendered
|
|
191
|
-
expect(screen.queryByTestId('modal-form-footer')).not.toBeInTheDocument();
|
|
192
|
-
});
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
describe('ModalForm Container Query Layout', () => {
|
|
196
|
-
/** CSS selector for the @container query context element */
|
|
197
|
-
const CONTAINER_SELECTOR = '.\\@container';
|
|
198
|
-
|
|
199
|
-
it('applies @container class on scrollable content area', async () => {
|
|
200
|
-
const mockDataSource = createMockDataSource();
|
|
201
|
-
|
|
202
|
-
render(
|
|
203
|
-
<ModalForm
|
|
204
|
-
schema={{
|
|
205
|
-
type: 'object-form',
|
|
206
|
-
formType: 'modal',
|
|
207
|
-
objectName: 'events',
|
|
208
|
-
mode: 'create',
|
|
209
|
-
title: 'Create Event',
|
|
210
|
-
open: true,
|
|
211
|
-
}}
|
|
212
|
-
dataSource={mockDataSource as any}
|
|
213
|
-
/>
|
|
214
|
-
);
|
|
215
|
-
|
|
216
|
-
await waitFor(() => {
|
|
217
|
-
expect(screen.getByText('Create Event')).toBeInTheDocument();
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
// The scrollable content wrapper should be a @container query context
|
|
221
|
-
const dialogContent = document.querySelector('[role="dialog"]');
|
|
222
|
-
expect(dialogContent).not.toBeNull();
|
|
223
|
-
const scrollArea = dialogContent!.querySelector(CONTAINER_SELECTOR);
|
|
224
|
-
expect(scrollArea).not.toBeNull();
|
|
225
|
-
expect(scrollArea!.className).toContain('overflow-y-auto');
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
it('uses container-query grid classes for multi-column flat fields', async () => {
|
|
229
|
-
// Mock schema with enough fields to trigger auto-layout 2-column
|
|
230
|
-
const manyFieldsSchema = {
|
|
231
|
-
name: 'contacts',
|
|
232
|
-
fields: {
|
|
233
|
-
name: { label: 'Name', type: 'text', required: true },
|
|
234
|
-
email: { label: 'Email', type: 'email', required: false },
|
|
235
|
-
phone: { label: 'Phone', type: 'phone', required: false },
|
|
236
|
-
company: { label: 'Company', type: 'text', required: false },
|
|
237
|
-
department: { label: 'Department', type: 'text', required: false },
|
|
238
|
-
title: { label: 'Title', type: 'text', required: false },
|
|
239
|
-
},
|
|
240
|
-
};
|
|
241
|
-
const mockDataSource = createMockDataSource();
|
|
242
|
-
mockDataSource.getObjectSchema.mockResolvedValue(manyFieldsSchema);
|
|
243
|
-
|
|
244
|
-
render(
|
|
245
|
-
<ModalForm
|
|
246
|
-
schema={{
|
|
247
|
-
type: 'object-form',
|
|
248
|
-
formType: 'modal',
|
|
249
|
-
objectName: 'contacts',
|
|
250
|
-
mode: 'create',
|
|
251
|
-
title: 'Create Contact',
|
|
252
|
-
open: true,
|
|
253
|
-
}}
|
|
254
|
-
dataSource={mockDataSource as any}
|
|
255
|
-
/>
|
|
256
|
-
);
|
|
257
|
-
|
|
258
|
-
await waitFor(() => {
|
|
259
|
-
expect(screen.getByText('Create Contact')).toBeInTheDocument();
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
// Wait for fields to render
|
|
263
|
-
await waitFor(() => {
|
|
264
|
-
expect(screen.getByText('Name')).toBeInTheDocument();
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
// The form field container should use container-query classes (@md:grid-cols-2)
|
|
268
|
-
// instead of viewport-based classes (md:grid-cols-2)
|
|
269
|
-
const dialogContent = document.querySelector('[role="dialog"]');
|
|
270
|
-
const containerEl = dialogContent!.querySelector(CONTAINER_SELECTOR);
|
|
271
|
-
expect(containerEl).not.toBeNull();
|
|
272
|
-
|
|
273
|
-
// Look for the grid container with @md:grid-cols-2
|
|
274
|
-
const gridEl = containerEl!.querySelector('[class*="@md:grid-cols-2"]');
|
|
275
|
-
expect(gridEl).not.toBeNull();
|
|
276
|
-
expect(gridEl!.className).toContain('@md:grid-cols-2');
|
|
277
|
-
// Should NOT use viewport-based md:grid-cols-2 (without @ prefix)
|
|
278
|
-
expect(gridEl!.className).not.toContain(' md:grid-cols-2');
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
it('single-column forms do not get container grid override', async () => {
|
|
282
|
-
// Only 3 fields → auto-layout stays at 1 column
|
|
283
|
-
const fewFieldsSchema = {
|
|
284
|
-
name: 'notes',
|
|
285
|
-
fields: {
|
|
286
|
-
title: { label: 'Title', type: 'text', required: true },
|
|
287
|
-
body: { label: 'Body', type: 'textarea', required: false },
|
|
288
|
-
status: { label: 'Status', type: 'select', required: false, options: [{ value: 'draft', label: 'Draft' }] },
|
|
289
|
-
},
|
|
290
|
-
};
|
|
291
|
-
const mockDataSource = createMockDataSource();
|
|
292
|
-
mockDataSource.getObjectSchema.mockResolvedValue(fewFieldsSchema);
|
|
293
|
-
|
|
294
|
-
render(
|
|
295
|
-
<ModalForm
|
|
296
|
-
schema={{
|
|
297
|
-
type: 'object-form',
|
|
298
|
-
formType: 'modal',
|
|
299
|
-
objectName: 'notes',
|
|
300
|
-
mode: 'create',
|
|
301
|
-
title: 'Create Note',
|
|
302
|
-
open: true,
|
|
303
|
-
}}
|
|
304
|
-
dataSource={mockDataSource as any}
|
|
305
|
-
/>
|
|
306
|
-
);
|
|
307
|
-
|
|
308
|
-
await waitFor(() => {
|
|
309
|
-
expect(screen.getByText('Create Note')).toBeInTheDocument();
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
await waitFor(() => {
|
|
313
|
-
expect(screen.getByText('Title')).toBeInTheDocument();
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
// Single column form should not have @md:grid-cols-2
|
|
317
|
-
const dialogContent = document.querySelector('[role="dialog"]');
|
|
318
|
-
const containerEl = dialogContent!.querySelector(CONTAINER_SELECTOR);
|
|
319
|
-
expect(containerEl).not.toBeNull();
|
|
320
|
-
const gridEl = containerEl!.querySelector('[class*="@md:grid-cols"]');
|
|
321
|
-
expect(gridEl).toBeNull();
|
|
322
|
-
});
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
describe('ModalForm Sections — Modal Size Auto-Upgrade', () => {
|
|
326
|
-
it('auto-upgrades modal to xl when sections use 2-column layout', async () => {
|
|
327
|
-
const mockDataSource = createMockDataSource();
|
|
328
|
-
|
|
329
|
-
render(
|
|
330
|
-
<ModalForm
|
|
331
|
-
schema={{
|
|
332
|
-
type: 'object-form',
|
|
333
|
-
formType: 'modal',
|
|
334
|
-
objectName: 'events',
|
|
335
|
-
mode: 'create',
|
|
336
|
-
title: 'Create Task',
|
|
337
|
-
open: true,
|
|
338
|
-
sections: [
|
|
339
|
-
{
|
|
340
|
-
label: 'Task Information',
|
|
341
|
-
columns: 2,
|
|
342
|
-
fields: ['subject', 'start', 'end', 'location'],
|
|
343
|
-
},
|
|
344
|
-
{
|
|
345
|
-
label: 'Details',
|
|
346
|
-
columns: 1,
|
|
347
|
-
fields: ['description'],
|
|
348
|
-
},
|
|
349
|
-
],
|
|
350
|
-
}}
|
|
351
|
-
dataSource={mockDataSource as any}
|
|
352
|
-
/>
|
|
353
|
-
);
|
|
354
|
-
|
|
355
|
-
await waitFor(() => {
|
|
356
|
-
expect(screen.getByText('Create Task')).toBeInTheDocument();
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
// Dialog should auto-upgrade to xl (max-w-5xl) because sections have columns: 2
|
|
360
|
-
const dialogContent = document.querySelector('[role="dialog"]');
|
|
361
|
-
expect(dialogContent).not.toBeNull();
|
|
362
|
-
expect(dialogContent!.className).toContain('max-w-5xl');
|
|
363
|
-
});
|
|
364
|
-
|
|
365
|
-
it('keeps default size when all sections use 1-column layout', async () => {
|
|
366
|
-
const mockDataSource = createMockDataSource();
|
|
367
|
-
|
|
368
|
-
render(
|
|
369
|
-
<ModalForm
|
|
370
|
-
schema={{
|
|
371
|
-
type: 'object-form',
|
|
372
|
-
formType: 'modal',
|
|
373
|
-
objectName: 'events',
|
|
374
|
-
mode: 'create',
|
|
375
|
-
title: 'Create Task',
|
|
376
|
-
open: true,
|
|
377
|
-
sections: [
|
|
378
|
-
{
|
|
379
|
-
label: 'Basic Info',
|
|
380
|
-
columns: 1,
|
|
381
|
-
fields: ['subject', 'start'],
|
|
382
|
-
},
|
|
383
|
-
],
|
|
384
|
-
}}
|
|
385
|
-
dataSource={mockDataSource as any}
|
|
386
|
-
/>
|
|
387
|
-
);
|
|
388
|
-
|
|
389
|
-
await waitFor(() => {
|
|
390
|
-
expect(screen.getByText('Create Task')).toBeInTheDocument();
|
|
391
|
-
});
|
|
392
|
-
|
|
393
|
-
// Dialog should remain at default size (max-w-lg)
|
|
394
|
-
const dialogContent = document.querySelector('[role="dialog"]');
|
|
395
|
-
expect(dialogContent).not.toBeNull();
|
|
396
|
-
expect(dialogContent!.className).toContain('max-w-lg');
|
|
397
|
-
});
|
|
398
|
-
|
|
399
|
-
it('respects explicit modalSize over section auto-upgrade', async () => {
|
|
400
|
-
const mockDataSource = createMockDataSource();
|
|
401
|
-
|
|
402
|
-
render(
|
|
403
|
-
<ModalForm
|
|
404
|
-
schema={{
|
|
405
|
-
type: 'object-form',
|
|
406
|
-
formType: 'modal',
|
|
407
|
-
objectName: 'events',
|
|
408
|
-
mode: 'create',
|
|
409
|
-
title: 'Create Task',
|
|
410
|
-
open: true,
|
|
411
|
-
modalSize: 'sm',
|
|
412
|
-
sections: [
|
|
413
|
-
{
|
|
414
|
-
label: 'Task Information',
|
|
415
|
-
columns: 2,
|
|
416
|
-
fields: ['subject', 'start', 'end', 'location'],
|
|
417
|
-
},
|
|
418
|
-
],
|
|
419
|
-
}}
|
|
420
|
-
dataSource={mockDataSource as any}
|
|
421
|
-
/>
|
|
422
|
-
);
|
|
423
|
-
|
|
424
|
-
await waitFor(() => {
|
|
425
|
-
expect(screen.getByText('Create Task')).toBeInTheDocument();
|
|
426
|
-
});
|
|
427
|
-
|
|
428
|
-
// Explicit modalSize: 'sm' should override section auto-upgrade
|
|
429
|
-
const dialogContent = document.querySelector('[role="dialog"]');
|
|
430
|
-
expect(dialogContent).not.toBeNull();
|
|
431
|
-
expect(dialogContent!.className).toContain('max-w-sm');
|
|
432
|
-
});
|
|
433
|
-
});
|