@object-ui/plugin-detail 3.0.2 → 3.1.0
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/.turbo/turbo-build.log +45 -8
- package/CHANGELOG.md +9 -0
- package/dist/AddressField-C07oUOY6.js +96 -0
- package/dist/AutoNumberField-BxnFqllo.js +8 -0
- package/dist/AvatarField-VThNABzo.js +82 -0
- package/dist/BooleanField-CGHKBzAi.js +37 -0
- package/dist/CodeField-Co_muhRR.js +21 -0
- package/dist/ColorField-DLid_tFz.js +42 -0
- package/dist/CurrencyField-Bw-LqANM.js +43 -0
- package/dist/DateField-BNHAzMB2.js +21 -0
- package/dist/DateTimeField-DjAyn_DQ.js +28 -0
- package/dist/EmailField-xoNcSppb.js +31 -0
- package/dist/FileField-DbNJwjU2.js +133 -0
- package/dist/FormulaField-CJkkwIK8.js +9 -0
- package/dist/GeolocationField-C1AnS6VV.js +123 -0
- package/dist/GridField-DATAHIKf.js +30 -0
- package/dist/ImageField-CEKJpyJp.js +90 -0
- package/dist/LocationField-jDWXjlpx.js +31 -0
- package/dist/LookupField-DQ08L9UQ.js +96 -0
- package/dist/MasterDetailField-Dbk529Ea.js +108 -0
- package/dist/NumberField-BVroN9aV.js +26 -0
- package/dist/ObjectField-CT3l_IHW.js +48 -0
- package/dist/PasswordField-DweVLEE0.js +38 -0
- package/dist/PercentField-ZpWUK97K.js +63 -0
- package/dist/PhoneField-mw-9fqZ_.js +31 -0
- package/dist/QRCodeField-Cbb9ck59.js +77 -0
- package/dist/RatingField-CSqgLS6t.js +47 -0
- package/dist/RichTextField-BpfBOd99.js +38 -0
- package/dist/SelectField-B9Ei-5jl.js +26 -0
- package/dist/SignatureField-DgGpHnQ8.js +85 -0
- package/dist/SliderField-C6HvOHd8.js +30 -0
- package/dist/SummaryField-ugYPYxjP.js +9 -0
- package/dist/TextAreaField-BK3RgzY3.js +39 -0
- package/dist/TextField-Bvzx3atT.js +32 -0
- package/dist/TimeField-Cuz9-Uai.js +21 -0
- package/dist/UrlField-B6XHTV73.js +33 -0
- package/dist/UserField-ooTul2d6.js +49 -0
- package/dist/VectorField-CKg9jdGa.js +25 -0
- package/dist/index-CnlyRfY_.js +59461 -0
- package/dist/index.js +30 -55026
- package/dist/index.umd.cjs +41 -30
- package/dist/plugin-detail.css +1 -1
- package/dist/src/ActivityTimeline.d.ts +20 -0
- package/dist/src/ActivityTimeline.d.ts.map +1 -0
- package/dist/src/CommentAttachment.d.ts +25 -0
- package/dist/src/CommentAttachment.d.ts.map +1 -0
- package/dist/src/CommentInput.d.ts +24 -0
- package/dist/src/CommentInput.d.ts.map +1 -0
- package/dist/src/DetailSection.d.ts +6 -0
- package/dist/src/DetailSection.d.ts.map +1 -1
- package/dist/src/DetailView.d.ts +4 -0
- package/dist/src/DetailView.d.ts.map +1 -1
- package/dist/src/DetailView.stories.d.ts +8 -0
- package/dist/src/DetailView.stories.d.ts.map +1 -1
- package/dist/src/DiffView.d.ts +24 -0
- package/dist/src/DiffView.d.ts.map +1 -0
- package/dist/src/FieldChangeItem.d.ts +21 -0
- package/dist/src/FieldChangeItem.d.ts.map +1 -0
- package/dist/src/InlineCreateRelated.d.ts +32 -0
- package/dist/src/InlineCreateRelated.d.ts.map +1 -0
- package/dist/src/MentionAutocomplete.d.ts +43 -0
- package/dist/src/MentionAutocomplete.d.ts.map +1 -0
- package/dist/src/PointInTimeRestore.d.ts +28 -0
- package/dist/src/PointInTimeRestore.d.ts.map +1 -0
- package/dist/src/ReactionPicker.d.ts +25 -0
- package/dist/src/ReactionPicker.d.ts.map +1 -0
- package/dist/src/RecordActivityTimeline.d.ts +49 -0
- package/dist/src/RecordActivityTimeline.d.ts.map +1 -0
- package/dist/src/RecordChatterPanel.d.ts +48 -0
- package/dist/src/RecordChatterPanel.d.ts.map +1 -0
- package/dist/src/RecordComments.d.ts +20 -0
- package/dist/src/RecordComments.d.ts.map +1 -0
- package/dist/src/RecordNavigationEnhanced.d.ts +18 -0
- package/dist/src/RecordNavigationEnhanced.d.ts.map +1 -0
- package/dist/src/RelatedList.d.ts +4 -0
- package/dist/src/RelatedList.d.ts.map +1 -1
- package/dist/src/RelationshipGraph.d.ts +23 -0
- package/dist/src/RelationshipGraph.d.ts.map +1 -0
- package/dist/src/RichTextCommentInput.d.ts +24 -0
- package/dist/src/RichTextCommentInput.d.ts.map +1 -0
- package/dist/src/SubscriptionToggle.d.ts +22 -0
- package/dist/src/SubscriptionToggle.d.ts.map +1 -0
- package/dist/src/ThreadedReplies.d.ts +26 -0
- package/dist/src/ThreadedReplies.d.ts.map +1 -0
- package/dist/src/autoLayout.d.ts +34 -0
- package/dist/src/autoLayout.d.ts.map +1 -0
- package/dist/src/index.d.ts +36 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/useDetailTranslation.d.ts +34 -0
- package/dist/src/useDetailTranslation.d.ts.map +1 -0
- package/package.json +8 -7
- package/src/ActivityTimeline.tsx +184 -0
- package/src/CommentAttachment.tsx +192 -0
- package/src/CommentInput.tsx +81 -0
- package/src/DetailSection.tsx +74 -9
- package/src/DetailView.stories.tsx +76 -0
- package/src/DetailView.tsx +270 -27
- package/src/DiffView.tsx +231 -0
- package/src/FieldChangeItem.tsx +46 -0
- package/src/InlineCreateRelated.tsx +291 -0
- package/src/MentionAutocomplete.tsx +123 -0
- package/src/PointInTimeRestore.tsx +261 -0
- package/src/ReactionPicker.tsx +106 -0
- package/src/RecordActivityTimeline.tsx +429 -0
- package/src/RecordChatterPanel.tsx +202 -0
- package/src/RecordComments.tsx +215 -0
- package/src/RecordNavigationEnhanced.tsx +211 -0
- package/src/RelatedList.tsx +37 -8
- package/src/RelationshipGraph.tsx +286 -0
- package/src/RichTextCommentInput.tsx +348 -0
- package/src/SubscriptionToggle.tsx +60 -0
- package/src/ThreadedReplies.tsx +161 -0
- package/src/__tests__/ActivityTimeline.test.tsx +119 -0
- package/src/__tests__/ActivityTimelineFiltering.test.tsx +143 -0
- package/src/__tests__/CommentInput.test.tsx +57 -0
- package/src/__tests__/DetailSection.test.tsx +320 -0
- package/src/__tests__/DetailView.test.tsx +415 -1
- package/src/__tests__/FieldChangeItem.test.tsx +119 -0
- package/src/__tests__/MentionAutocomplete.test.tsx +97 -0
- package/src/__tests__/ReactionPicker.test.tsx +113 -0
- package/src/__tests__/RecordActivityTimeline.test.tsx +395 -0
- package/src/__tests__/RecordChatterPanel.test.tsx +227 -0
- package/src/__tests__/RecordComments.test.tsx +96 -0
- package/src/__tests__/RecordCommentsPinSearch.test.tsx +133 -0
- package/src/__tests__/RelatedList.test.tsx +66 -0
- package/src/__tests__/SubscriptionToggle.test.tsx +84 -0
- package/src/__tests__/ThreadedReplies.test.tsx +212 -0
- package/src/__tests__/autoLayout.test.ts +184 -0
- package/src/__tests__/phase12-features.test.tsx +583 -0
- package/src/autoLayout.ts +111 -0
- package/src/index.tsx +46 -0
- package/src/useDetailTranslation.ts +103 -0
|
@@ -0,0 +1,320 @@
|
|
|
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 } from 'vitest';
|
|
10
|
+
import { render, screen } from '@testing-library/react';
|
|
11
|
+
import { DetailSection } from '../DetailSection';
|
|
12
|
+
|
|
13
|
+
describe('DetailSection', () => {
|
|
14
|
+
it('should render text fields as plain text', () => {
|
|
15
|
+
const section = {
|
|
16
|
+
title: 'Info',
|
|
17
|
+
fields: [{ name: 'name', label: 'Name', type: 'text' }],
|
|
18
|
+
columns: 1,
|
|
19
|
+
};
|
|
20
|
+
render(<DetailSection section={section} data={{ name: 'Alice' }} />);
|
|
21
|
+
expect(screen.getByText('Alice')).toBeInTheDocument();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should render date fields formatted (not raw ISO)', () => {
|
|
25
|
+
const section = {
|
|
26
|
+
title: 'Info',
|
|
27
|
+
fields: [{ name: 'order_date', label: 'Order Date', type: 'date' }],
|
|
28
|
+
columns: 1,
|
|
29
|
+
};
|
|
30
|
+
render(<DetailSection section={section} data={{ order_date: '2024-01-15T00:00:00.000Z' }} />);
|
|
31
|
+
// Should NOT show raw ISO string
|
|
32
|
+
expect(screen.queryByText('2024-01-15T00:00:00.000Z')).not.toBeInTheDocument();
|
|
33
|
+
// Should show formatted date (e.g. "Jan 15, 2024")
|
|
34
|
+
expect(screen.getByText(/Jan/)).toBeInTheDocument();
|
|
35
|
+
expect(screen.getByText(/2024/)).toBeInTheDocument();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should render currency fields formatted', () => {
|
|
39
|
+
const section = {
|
|
40
|
+
title: 'Info',
|
|
41
|
+
fields: [{ name: 'total_amount', label: 'Total Amount', type: 'currency' }],
|
|
42
|
+
columns: 1,
|
|
43
|
+
};
|
|
44
|
+
render(<DetailSection section={section} data={{ total_amount: 15459.99 }} />);
|
|
45
|
+
// Should NOT show plain number
|
|
46
|
+
expect(screen.queryByText('15459.99')).not.toBeInTheDocument();
|
|
47
|
+
// Should show formatted currency (e.g. "$15,459.99")
|
|
48
|
+
expect(screen.getByText(/15,459\.99/)).toBeInTheDocument();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should render boolean fields with checkbox', () => {
|
|
52
|
+
const section = {
|
|
53
|
+
title: 'Info',
|
|
54
|
+
fields: [{ name: 'active', label: 'Active', type: 'boolean' }],
|
|
55
|
+
columns: 1,
|
|
56
|
+
};
|
|
57
|
+
render(<DetailSection section={section} data={{ active: true }} />);
|
|
58
|
+
// BooleanCellRenderer renders a checkbox
|
|
59
|
+
const checkbox = screen.getByRole('checkbox');
|
|
60
|
+
expect(checkbox).toBeInTheDocument();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should render select fields as badge', () => {
|
|
64
|
+
const section = {
|
|
65
|
+
title: 'Info',
|
|
66
|
+
fields: [
|
|
67
|
+
{
|
|
68
|
+
name: 'status',
|
|
69
|
+
label: 'Status',
|
|
70
|
+
type: 'select',
|
|
71
|
+
options: [
|
|
72
|
+
{ value: 'Draft', label: 'Draft', color: 'yellow' },
|
|
73
|
+
{ value: 'Active', label: 'Active', color: 'green' },
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
columns: 1,
|
|
78
|
+
};
|
|
79
|
+
render(<DetailSection section={section} data={{ status: 'Draft' }} />);
|
|
80
|
+
expect(screen.getByText('Draft')).toBeInTheDocument();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should render null/undefined values as dash', () => {
|
|
84
|
+
const section = {
|
|
85
|
+
title: 'Info',
|
|
86
|
+
fields: [{ name: 'missing', label: 'Missing', type: 'text' }],
|
|
87
|
+
columns: 1,
|
|
88
|
+
};
|
|
89
|
+
render(<DetailSection section={section} data={{}} />);
|
|
90
|
+
expect(screen.getByText('—')).toBeInTheDocument();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should render section title', () => {
|
|
94
|
+
const section = {
|
|
95
|
+
title: 'Basic Information',
|
|
96
|
+
fields: [{ name: 'name', label: 'Name' }],
|
|
97
|
+
columns: 1,
|
|
98
|
+
};
|
|
99
|
+
render(<DetailSection section={section} data={{ name: 'Test' }} />);
|
|
100
|
+
expect(screen.getByText('Basic Information')).toBeInTheDocument();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should auto-infer 2 columns when columns is not set and 5+ fields exist', () => {
|
|
104
|
+
const section = {
|
|
105
|
+
title: 'Auto Layout',
|
|
106
|
+
fields: Array.from({ length: 6 }, (_, i) => ({
|
|
107
|
+
name: `field_${i}`,
|
|
108
|
+
label: `Field ${i}`,
|
|
109
|
+
type: 'text',
|
|
110
|
+
})),
|
|
111
|
+
};
|
|
112
|
+
const { container } = render(
|
|
113
|
+
<DetailSection section={section} data={{}} />
|
|
114
|
+
);
|
|
115
|
+
// The grid container should have the sm:grid-cols-2 class
|
|
116
|
+
const grid = container.querySelector('.grid');
|
|
117
|
+
expect(grid).toBeTruthy();
|
|
118
|
+
expect(grid!.className).toContain('md:grid-cols-2');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('should auto-infer 3 columns when columns is not set and 11+ fields exist', () => {
|
|
122
|
+
const section = {
|
|
123
|
+
title: 'Many Fields',
|
|
124
|
+
fields: Array.from({ length: 12 }, (_, i) => ({
|
|
125
|
+
name: `field_${i}`,
|
|
126
|
+
label: `Field ${i}`,
|
|
127
|
+
type: 'text',
|
|
128
|
+
})),
|
|
129
|
+
};
|
|
130
|
+
const { container } = render(
|
|
131
|
+
<DetailSection section={section} data={{}} />
|
|
132
|
+
);
|
|
133
|
+
const grid = container.querySelector('.grid');
|
|
134
|
+
expect(grid).toBeTruthy();
|
|
135
|
+
expect(grid!.className).toContain('lg:grid-cols-3');
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should keep 1 column when columns is not set and ≤3 fields exist', () => {
|
|
139
|
+
const section = {
|
|
140
|
+
title: 'Few Fields',
|
|
141
|
+
fields: [
|
|
142
|
+
{ name: 'a', label: 'A', type: 'text' },
|
|
143
|
+
{ name: 'b', label: 'B', type: 'text' },
|
|
144
|
+
],
|
|
145
|
+
};
|
|
146
|
+
const { container } = render(
|
|
147
|
+
<DetailSection section={section} data={{}} />
|
|
148
|
+
);
|
|
149
|
+
const grid = container.querySelector('.grid');
|
|
150
|
+
expect(grid).toBeTruthy();
|
|
151
|
+
expect(grid!.className).toContain('grid-cols-1');
|
|
152
|
+
expect(grid!.className).not.toContain('sm:grid-cols-2');
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('should respect explicit columns=1 even with many fields', () => {
|
|
156
|
+
const section = {
|
|
157
|
+
title: 'Forced Single Column',
|
|
158
|
+
fields: Array.from({ length: 15 }, (_, i) => ({
|
|
159
|
+
name: `field_${i}`,
|
|
160
|
+
label: `Field ${i}`,
|
|
161
|
+
type: 'text',
|
|
162
|
+
})),
|
|
163
|
+
columns: 1,
|
|
164
|
+
};
|
|
165
|
+
const { container } = render(
|
|
166
|
+
<DetailSection section={section} data={{}} />
|
|
167
|
+
);
|
|
168
|
+
const grid = container.querySelector('.grid');
|
|
169
|
+
expect(grid).toBeTruthy();
|
|
170
|
+
expect(grid!.className).toContain('grid-cols-1');
|
|
171
|
+
expect(grid!.className).not.toContain('sm:grid-cols-2');
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('should hide empty fields when hideEmpty is true', () => {
|
|
175
|
+
const section = {
|
|
176
|
+
title: 'Info',
|
|
177
|
+
hideEmpty: true,
|
|
178
|
+
fields: [
|
|
179
|
+
{ name: 'name', label: 'Name', type: 'text' },
|
|
180
|
+
{ name: 'email', label: 'Email', type: 'text' },
|
|
181
|
+
{ name: 'phone', label: 'Phone', type: 'text' },
|
|
182
|
+
],
|
|
183
|
+
columns: 1,
|
|
184
|
+
};
|
|
185
|
+
render(<DetailSection section={section} data={{ name: 'Alice', email: null, phone: '' }} />);
|
|
186
|
+
expect(screen.getByText('Alice')).toBeInTheDocument();
|
|
187
|
+
expect(screen.queryByText('Email')).not.toBeInTheDocument();
|
|
188
|
+
expect(screen.queryByText('Phone')).not.toBeInTheDocument();
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('should hide entire section when all fields are empty and hideEmpty is true', () => {
|
|
192
|
+
const section = {
|
|
193
|
+
title: 'Empty Section',
|
|
194
|
+
hideEmpty: true,
|
|
195
|
+
fields: [
|
|
196
|
+
{ name: 'a', label: 'A', type: 'text' },
|
|
197
|
+
{ name: 'b', label: 'B', type: 'text' },
|
|
198
|
+
],
|
|
199
|
+
columns: 1,
|
|
200
|
+
};
|
|
201
|
+
const { container } = render(<DetailSection section={section} data={{ a: null, b: undefined }} />);
|
|
202
|
+
// Section should be hidden entirely
|
|
203
|
+
expect(container.innerHTML).toBe('');
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('should still show empty fields when hideEmpty is not set', () => {
|
|
207
|
+
const section = {
|
|
208
|
+
title: 'Info',
|
|
209
|
+
fields: [
|
|
210
|
+
{ name: 'name', label: 'Name', type: 'text' },
|
|
211
|
+
{ name: 'missing', label: 'Missing', type: 'text' },
|
|
212
|
+
],
|
|
213
|
+
columns: 1,
|
|
214
|
+
};
|
|
215
|
+
render(<DetailSection section={section} data={{ name: 'Alice' }} />);
|
|
216
|
+
expect(screen.getByText('Alice')).toBeInTheDocument();
|
|
217
|
+
expect(screen.getByText('—')).toBeInTheDocument();
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('should use md: breakpoint for 2-column layouts', () => {
|
|
221
|
+
const section = {
|
|
222
|
+
title: 'Responsive',
|
|
223
|
+
fields: Array.from({ length: 6 }, (_, i) => ({
|
|
224
|
+
name: `field_${i}`,
|
|
225
|
+
label: `Field ${i}`,
|
|
226
|
+
type: 'text',
|
|
227
|
+
})),
|
|
228
|
+
};
|
|
229
|
+
const { container } = render(
|
|
230
|
+
<DetailSection section={section} data={{}} />
|
|
231
|
+
);
|
|
232
|
+
const grid = container.querySelector('.grid');
|
|
233
|
+
expect(grid).toBeTruthy();
|
|
234
|
+
expect(grid!.className).toContain('md:grid-cols-2');
|
|
235
|
+
expect(grid!.className).not.toContain('sm:grid-cols-2');
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it('should use lg: breakpoint for 3-column layouts', () => {
|
|
239
|
+
const section = {
|
|
240
|
+
title: 'Responsive',
|
|
241
|
+
fields: Array.from({ length: 12 }, (_, i) => ({
|
|
242
|
+
name: `field_${i}`,
|
|
243
|
+
label: `Field ${i}`,
|
|
244
|
+
type: 'text',
|
|
245
|
+
})),
|
|
246
|
+
};
|
|
247
|
+
const { container } = render(
|
|
248
|
+
<DetailSection section={section} data={{}} />
|
|
249
|
+
);
|
|
250
|
+
const grid = container.querySelector('.grid');
|
|
251
|
+
expect(grid).toBeTruthy();
|
|
252
|
+
expect(grid!.className).toContain('lg:grid-cols-3');
|
|
253
|
+
expect(grid!.className).not.toContain('md:grid-cols-3');
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it('should enrich field type from objectSchema when field.type is not set', () => {
|
|
257
|
+
const section = {
|
|
258
|
+
title: 'Info',
|
|
259
|
+
fields: [{ name: 'status', label: 'Status' }],
|
|
260
|
+
columns: 1,
|
|
261
|
+
};
|
|
262
|
+
const objectSchema = {
|
|
263
|
+
fields: {
|
|
264
|
+
status: {
|
|
265
|
+
type: 'select',
|
|
266
|
+
options: [
|
|
267
|
+
{ value: 'Draft', label: 'Draft', color: 'yellow' },
|
|
268
|
+
{ value: 'Active', label: 'Active', color: 'green' },
|
|
269
|
+
],
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
};
|
|
273
|
+
render(<DetailSection section={section} data={{ status: 'Draft' }} objectSchema={objectSchema} />);
|
|
274
|
+
// Should render via SelectCellRenderer (displays label), not plain String()
|
|
275
|
+
expect(screen.getByText('Draft')).toBeInTheDocument();
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it('should render percent field from objectSchema enrichment', () => {
|
|
279
|
+
const section = {
|
|
280
|
+
title: 'Info',
|
|
281
|
+
fields: [{ name: 'discount', label: 'Discount' }],
|
|
282
|
+
columns: 1,
|
|
283
|
+
};
|
|
284
|
+
const objectSchema = {
|
|
285
|
+
fields: {
|
|
286
|
+
discount: { type: 'percent' },
|
|
287
|
+
},
|
|
288
|
+
};
|
|
289
|
+
render(<DetailSection section={section} data={{ discount: 25 }} objectSchema={objectSchema} />);
|
|
290
|
+
// PercentCellRenderer should format as "25%"
|
|
291
|
+
expect(screen.getByText(/25/)).toBeInTheDocument();
|
|
292
|
+
expect(screen.getByText(/%/)).toBeInTheDocument();
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it('should fall back to String(value) when neither field.type nor objectSchema provides a type', () => {
|
|
296
|
+
const section = {
|
|
297
|
+
title: 'Info',
|
|
298
|
+
fields: [{ name: 'notes', label: 'Notes' }],
|
|
299
|
+
columns: 1,
|
|
300
|
+
};
|
|
301
|
+
render(<DetailSection section={section} data={{ notes: 'Hello World' }} />);
|
|
302
|
+
expect(screen.getByText('Hello World')).toBeInTheDocument();
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it('should prefer explicit field.type over objectSchema type', () => {
|
|
306
|
+
const section = {
|
|
307
|
+
title: 'Info',
|
|
308
|
+
fields: [{ name: 'name', label: 'Name', type: 'text' as const }],
|
|
309
|
+
columns: 1,
|
|
310
|
+
};
|
|
311
|
+
const objectSchema = {
|
|
312
|
+
fields: {
|
|
313
|
+
name: { type: 'number' },
|
|
314
|
+
},
|
|
315
|
+
};
|
|
316
|
+
render(<DetailSection section={section} data={{ name: 'Alice' }} objectSchema={objectSchema} />);
|
|
317
|
+
// Should use 'text' renderer, not 'number'
|
|
318
|
+
expect(screen.getByText('Alice')).toBeInTheDocument();
|
|
319
|
+
});
|
|
320
|
+
});
|