Skip to content

Commit f5e034c

Browse files
committed
Policy Field: Add MaxLen, Required Priority and Fix Decimal issues
1 parent 0239de6 commit f5e034c

5 files changed

Lines changed: 80 additions & 7 deletions

File tree

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
'@red-hat-developer-hub/backstage-plugin-dcm': patch
3+
---
4+
5+
Fix policy form validation for Description and Priority fields, normalize enabled
6+
checks, and reject whitespace-only required fields.
7+
8+
- Add a 255-character max-length Yup rule to the Description field, with inline error
9+
feedback matching the Display Name pattern (validation error on blur rather than
10+
hard-blocking input at the DOM level).
11+
- Add `.integer()` and `.required()` Yup rules to the Priority field so decimal values
12+
(e.g. 500.5) and an empty field are rejected with an explicit error message instead of
13+
silently coercing to an integer or defaulting to 500.
14+
- Add `step={1}` to the Priority number input and block `.`/`e`/`E` characters via
15+
`onKeyDown` and `onChange` guards to prevent decimal strings from bypassing the Yup
16+
integer check through JavaScript's `Number()` coercion.
17+
- Update the Priority label to "Priority \*" to indicate it is a required field.
18+
- Update the Priority helper text to surface the API uniqueness constraint
19+
(priority must be unique per policy type) so users are informed before hitting a 409.
20+
- Normalize the three inconsistent `enabled` checks in `PoliciesTabContent` to
21+
`p.enabled ?? true` so the Enabled column, Actions toggle, and edit form all agree
22+
when `enabled` is undefined.
23+
- Add `.trim()` to the `display_name` and `rego_code` Yup rules so whitespace-only
24+
values are rejected client-side instead of passing validation and being silently
25+
rejected by the API after submit.

workspaces/dcm/plugins/dcm/src/pages/policies/PoliciesTabContent.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ export function PoliciesTabContent() {
172172
title: 'Enabled',
173173
field: 'enabled',
174174
render: p => {
175-
const isEnabled = p.enabled === true;
175+
const isEnabled = p.enabled ?? true;
176176
return (
177177
<Chip
178178
label={isEnabled ? 'Yes' : 'No'}
@@ -204,7 +204,7 @@ export function PoliciesTabContent() {
204204
render: p => {
205205
const id = p.id ?? '';
206206
const isToggling = togglingIds.has(id);
207-
const isEnabled = p.enabled !== false;
207+
const isEnabled = p.enabled ?? true;
208208
return (
209209
<Box className={classes.actionsCellBox}>
210210
<Tooltip

workspaces/dcm/plugins/dcm/src/pages/policies/components/PolicyFormFields.tsx

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,15 @@ export function PolicyFormFields({
7171

7272
<TextField
7373
label="Description"
74-
helperText="Optional — describe the purpose of this policy"
74+
helperText={
75+
err('description') ?? 'Optional — describe the purpose of this policy'
76+
}
77+
error={Boolean(err('description'))}
7578
value={form.description}
7679
onChange={e =>
7780
setForm(prev => ({ ...prev, description: e.target.value }))
7881
}
82+
onBlur={() => touch('description')}
7983
fullWidth
8084
variant="outlined"
8185
size="small"
@@ -111,19 +115,29 @@ export function PolicyFormFields({
111115
</FormControl>
112116

113117
<TextField
114-
label="Priority"
118+
label="Priority *"
115119
helperText={
116-
err('priority') ?? '1 (highest) – 1000 (lowest), default 500'
120+
err('priority') ??
121+
'1 (highest) – 1000 (lowest), default 500 — must be unique per policy type'
117122
}
118123
error={Boolean(err('priority'))}
119124
value={form.priority}
120-
onChange={e => setForm(prev => ({ ...prev, priority: e.target.value }))}
125+
onChange={e => {
126+
if (/^\d*$/.test(e.target.value)) {
127+
setForm(prev => ({ ...prev, priority: e.target.value }));
128+
}
129+
}}
130+
onKeyDown={e => {
131+
if (e.key === '.' || e.key === 'e' || e.key === 'E') {
132+
e.preventDefault();
133+
}
134+
}}
121135
onBlur={() => touch('priority')}
122136
fullWidth
123137
variant="outlined"
124138
size="small"
125139
type="number"
126-
inputProps={{ min: 1, max: 1000 }}
140+
inputProps={{ min: 1, max: 1000, step: 1 }}
127141
/>
128142

129143
<TextField

workspaces/dcm/plugins/dcm/src/pages/policies/policyFormTypes.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,21 @@ describe('validatePolicyForm', () => {
3838
expect(errors.display_name).toBeDefined();
3939
});
4040

41+
it('rejects whitespace-only display_name', () => {
42+
const errors = validatePolicyForm({ ...valid(), display_name: ' ' });
43+
expect(errors.display_name).toBeDefined();
44+
});
45+
4146
it('requires rego_code', () => {
4247
const errors = validatePolicyForm({ ...valid(), rego_code: '' });
4348
expect(errors.rego_code).toBeDefined();
4449
});
4550

51+
it('rejects whitespace-only rego_code', () => {
52+
const errors = validatePolicyForm({ ...valid(), rego_code: ' ' });
53+
expect(errors.rego_code).toBeDefined();
54+
});
55+
4656
it('validates priority range', () => {
4757
const tooLow = validatePolicyForm({ ...valid(), priority: '0' });
4858
expect(tooLow.priority).toBeDefined();
@@ -52,6 +62,23 @@ describe('validatePolicyForm', () => {
5262

5363
const ok = validatePolicyForm({ ...valid(), priority: '500' });
5464
expect(ok.priority).toBeUndefined();
65+
66+
const decimal = validatePolicyForm({ ...valid(), priority: '500.5' });
67+
expect(decimal.priority).toBeDefined();
68+
69+
const empty = validatePolicyForm({ ...valid(), priority: '' });
70+
expect(empty.priority).toBeDefined();
71+
});
72+
73+
it('rejects description longer than 255 characters', () => {
74+
const errors = validatePolicyForm({
75+
...valid(),
76+
description: 'a'.repeat(256),
77+
});
78+
expect(errors.description).toBeDefined();
79+
80+
const ok = validatePolicyForm({ ...valid(), description: 'a'.repeat(255) });
81+
expect(ok.description).toBeUndefined();
5582
});
5683

5784
it('accepts only GLOBAL or USER as policy_type', () => {

workspaces/dcm/plugins/dcm/src/pages/policies/policyFormTypes.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,20 +34,27 @@ export type PolicyForm = {
3434
const policySchema = yup.object({
3535
display_name: yup
3636
.string()
37+
.trim()
3738
.required('Display name is required')
3839
.min(1, 'Display name cannot be empty')
3940
.max(255, 'Display name must be at most 255 characters'),
41+
description: yup
42+
.string()
43+
.max(255, 'Description must be at most 255 characters'),
4044
policy_type: yup
4145
.string()
4246
.required('Policy type is required')
4347
.oneOf(['GLOBAL', 'USER'], 'Must be GLOBAL or USER'),
4448
priority: yup
4549
.number()
4650
.typeError('Priority must be a number')
51+
.required('Priority is required')
52+
.integer('Priority must be a whole number')
4753
.min(1, 'Priority must be at least 1')
4854
.max(1000, 'Priority must be at most 1000'),
4955
rego_code: yup
5056
.string()
57+
.trim()
5158
.required('Rego code is required')
5259
.min(1, 'Rego code cannot be empty'),
5360
});

0 commit comments

Comments
 (0)