Skip to content

Commit e895318

Browse files
Copilothotlong
andcommitted
fix: render helpText in ConfigFieldRenderer for all field types including custom
The helpText property was defined on ConfigField type and set on navigation-dependent fields but never actually rendered. Refactored ConfigFieldRenderer to use break-based switch + post-render helpText wrapper. Added 3 new tests for helpText rendering. Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent f9ae512 commit e895318

2 files changed

Lines changed: 72 additions & 13 deletions

File tree

packages/components/src/__tests__/config-field-renderer.test.tsx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,4 +271,37 @@ describe('ConfigFieldRenderer', () => {
271271
expect(screen.getByText('Add sort')).toBeDefined();
272272
});
273273
});
274+
275+
describe('helpText rendering', () => {
276+
it('should render helpText below field when provided', () => {
277+
const field: ConfigField = {
278+
key: 'width',
279+
label: 'Width',
280+
type: 'input',
281+
helpText: 'Available for drawer, modal, and split modes',
282+
};
283+
render(<ConfigFieldRenderer field={field} value="" onChange={vi.fn()} draft={defaultDraft} />);
284+
expect(screen.getByText('Available for drawer, modal, and split modes')).toBeDefined();
285+
});
286+
287+
it('should not render helpText paragraph when not provided', () => {
288+
const field: ConfigField = { key: 'name', label: 'Name', type: 'input' };
289+
const { container } = render(<ConfigFieldRenderer field={field} value="" onChange={vi.fn()} draft={defaultDraft} />);
290+
expect(container.querySelectorAll('p').length).toBe(0);
291+
});
292+
293+
it('should render helpText for custom field type', () => {
294+
const React = require('react');
295+
const field: ConfigField = {
296+
key: 'custom',
297+
label: 'Custom',
298+
type: 'custom',
299+
helpText: 'Custom help text',
300+
render: (value, onChange) => React.createElement('div', { 'data-testid': 'custom-content' }, 'Custom'),
301+
};
302+
render(<ConfigFieldRenderer field={field} value="" onChange={vi.fn()} draft={defaultDraft} />);
303+
expect(screen.getByText('Custom help text')).toBeDefined();
304+
expect(screen.getByTestId('custom-content')).toBeDefined();
305+
});
306+
});
274307
});

packages/components/src/custom/config-field-renderer.tsx

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,11 @@ export function ConfigFieldRenderer({
6060
const effectiveDisabled = field.disabled || (field.disabledWhen ? field.disabledWhen(draft) : false);
6161
const effectiveValue = value ?? field.defaultValue;
6262

63+
let content: React.ReactNode = null;
64+
6365
switch (field.type) {
6466
case 'input':
65-
return (
67+
content = (
6668
<ConfigRow label={field.label}>
6769
<Input
6870
data-testid={`config-field-${field.key}`}
@@ -74,9 +76,10 @@ export function ConfigFieldRenderer({
7476
/>
7577
</ConfigRow>
7678
);
79+
break;
7780

7881
case 'switch':
79-
return (
82+
content = (
8083
<ConfigRow label={field.label}>
8184
<Switch
8285
data-testid={`config-field-${field.key}`}
@@ -87,9 +90,10 @@ export function ConfigFieldRenderer({
8790
/>
8891
</ConfigRow>
8992
);
93+
break;
9094

9195
case 'checkbox':
92-
return (
96+
content = (
9397
<ConfigRow label={field.label}>
9498
<Checkbox
9599
data-testid={`config-field-${field.key}`}
@@ -99,9 +103,10 @@ export function ConfigFieldRenderer({
99103
/>
100104
</ConfigRow>
101105
);
106+
break;
102107

103108
case 'select':
104-
return (
109+
content = (
105110
<ConfigRow label={field.label}>
106111
<Select
107112
value={String(effectiveValue ?? '')}
@@ -124,9 +129,10 @@ export function ConfigFieldRenderer({
124129
</Select>
125130
</ConfigRow>
126131
);
132+
break;
127133

128134
case 'slider':
129-
return (
135+
content = (
130136
<ConfigRow label={field.label}>
131137
<div className="flex items-center gap-2 w-32">
132138
<Slider
@@ -145,9 +151,10 @@ export function ConfigFieldRenderer({
145151
</div>
146152
</ConfigRow>
147153
);
154+
break;
148155

149156
case 'color':
150-
return (
157+
content = (
151158
<ConfigRow label={field.label}>
152159
<input
153160
data-testid={`config-field-${field.key}`}
@@ -159,9 +166,10 @@ export function ConfigFieldRenderer({
159166
/>
160167
</ConfigRow>
161168
);
169+
break;
162170

163171
case 'icon-group':
164-
return (
172+
content = (
165173
<ConfigRow label={field.label}>
166174
<div className="flex items-center gap-0.5" data-testid={`config-field-${field.key}`}>
167175
{(field.options ?? []).map((opt) => (
@@ -181,9 +189,10 @@ export function ConfigFieldRenderer({
181189
</div>
182190
</ConfigRow>
183191
);
192+
break;
184193

185194
case 'field-picker':
186-
return (
195+
content = (
187196
<ConfigRow
188197
label={field.label}
189198
value={effectiveValue ?? field.placeholder ?? 'Select field…'}
@@ -192,9 +201,10 @@ export function ConfigFieldRenderer({
192201
}}
193202
/>
194203
);
204+
break;
195205

196206
case 'filter':
197-
return (
207+
content = (
198208
<div data-testid={`config-field-${field.key}`}>
199209
<ConfigRow label={field.label} />
200210
<FilterBuilder
@@ -205,9 +215,10 @@ export function ConfigFieldRenderer({
205215
/>
206216
</div>
207217
);
218+
break;
208219

209220
case 'sort':
210-
return (
221+
content = (
211222
<div data-testid={`config-field-${field.key}`}>
212223
<ConfigRow label={field.label} />
213224
<SortBuilder
@@ -218,14 +229,29 @@ export function ConfigFieldRenderer({
218229
/>
219230
</div>
220231
);
232+
break;
221233

222234
case 'custom':
223235
if (field.render) {
224-
return <>{field.render(effectiveValue, onChange, draft)}</>;
236+
content = <>{field.render(effectiveValue, onChange, draft)}</>;
225237
}
226-
return null;
238+
break;
227239

228240
default:
229-
return null;
241+
break;
230242
}
243+
244+
if (!content) return null;
245+
246+
// Wrap with helpText when provided
247+
if (field.helpText) {
248+
return (
249+
<div>
250+
{content}
251+
<p className="text-[10px] text-muted-foreground mt-0.5 mb-1">{field.helpText}</p>
252+
</div>
253+
);
254+
}
255+
256+
return <>{content}</>;
231257
}

0 commit comments

Comments
 (0)