Skip to content

Commit 1fab320

Browse files
saltpy-csclaude
andauthored
feat: display evidence requirement descriptions in submission form (#237)
Pass full EvidenceRequirement[] to EvidenceSubmissionForm so the type selector shows human-readable descriptions instead of raw type strings. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent c64df12 commit 1fab320

3 files changed

Lines changed: 96 additions & 9 deletions

File tree

src/views/workflow-executions/partials/EvidenceSubmissionForm.vue

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
id="evidenceType"
1818
v-model="evidenceForm.type"
1919
:options="availableEvidenceTypes"
20+
optionLabel="label"
21+
optionValue="value"
2022
placeholder="Select evidence type"
2123
class="w-full"
2224
/>
@@ -106,6 +108,7 @@ import { ref, computed } from 'vue';
106108
import type {
107109
StepExecution,
108110
EvidenceType,
111+
EvidenceRequirement,
109112
StepExecutionEvidenceSubmit,
110113
} from '@/types/workflows';
111114
import Label from '@/volt/Label.vue';
@@ -117,7 +120,7 @@ import Message from '@/volt/Message.vue';
117120
118121
const props = defineProps<{
119122
step: StepExecution;
120-
evidenceRequirements: string[];
123+
evidenceRequirements: EvidenceRequirement[];
121124
}>();
122125
123126
const emit = defineEmits<{
@@ -140,21 +143,26 @@ const selectedFiles = ref<File[]>([]);
140143
const isSubmitting = ref(false);
141144
const submitError = ref('');
142145
143-
// Get evidence types from requirements, or allow all common types
144146
const availableEvidenceTypes = computed(() => {
147+
const toOption = (type: EvidenceType, description?: string) => ({
148+
value: type,
149+
label: description || type,
150+
});
151+
152+
if (props.evidenceRequirements.length > 0) {
153+
return props.evidenceRequirements.map((req) =>
154+
toOption(req.type, req.description),
155+
);
156+
}
157+
145158
const allTypes: EvidenceType[] = [
146159
'document',
147160
'attestation',
148161
'screenshot',
149162
'link',
150163
'text',
151164
];
152-
153-
if (props.evidenceRequirements.length > 0) {
154-
return props.evidenceRequirements;
155-
}
156-
157-
return allTypes;
165+
return allTypes.map((type) => toOption(type));
158166
});
159167
160168
const isFileEvidenceType = computed(() => {

src/views/workflow-executions/partials/StepExecutionPanel.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@
233233
<EvidenceSubmissionForm
234234
v-if="canSubmitEvidence"
235235
:step="step"
236-
:evidence-requirements="requiredEvidenceTypes"
236+
:evidence-requirements="evidenceRequirements"
237237
@evidence-submitted="handleEvidenceSubmitted"
238238
/>
239239

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { describe, it, expect } from 'vitest';
2+
import { mount } from '@vue/test-utils';
3+
import EvidenceSubmissionForm from '../EvidenceSubmissionForm.vue';
4+
import type { EvidenceRequirement, StepExecution } from '@/types/workflows';
5+
6+
const step = {
7+
id: 'step-1',
8+
status: 'in_progress',
9+
evidence: [],
10+
} as unknown as StepExecution;
11+
12+
const selectStub = {
13+
props: ['options', 'optionLabel', 'optionValue', 'modelValue', 'placeholder'],
14+
emits: ['update:modelValue'],
15+
template: `
16+
<div class="select-stub">
17+
<span
18+
v-for="(option, i) in options"
19+
:key="i"
20+
class="select-option"
21+
>{{ optionLabel ? option[optionLabel] : option }}</span>
22+
</div>
23+
`,
24+
};
25+
26+
const stubs = {
27+
Select: selectStub,
28+
Label: { template: '<label><slot /></label>' },
29+
Textarea: { template: '<textarea />' },
30+
InputText: { template: '<input />' },
31+
PrimaryButton: { template: '<button><slot /></button>' },
32+
Message: { template: '<div><slot /></div>' },
33+
};
34+
35+
describe('EvidenceSubmissionForm', () => {
36+
it('displays description from EvidenceRequirement objects in the type selector', () => {
37+
const requirements: EvidenceRequirement[] = [
38+
{
39+
type: 'document',
40+
required: true,
41+
description: 'Upload policy document',
42+
},
43+
{
44+
type: 'attestation',
45+
required: false,
46+
description: 'Sign off attestation',
47+
},
48+
];
49+
50+
const wrapper = mount(EvidenceSubmissionForm, {
51+
props: { step, evidenceRequirements: requirements },
52+
global: { stubs },
53+
});
54+
55+
const optionTexts = wrapper
56+
.findAll('.select-option')
57+
.map((o) => o.text().trim());
58+
59+
expect(optionTexts).toContain('Upload policy document');
60+
expect(optionTexts).toContain('Sign off attestation');
61+
});
62+
63+
it('falls back to the type name when a requirement has no description', () => {
64+
const requirements: EvidenceRequirement[] = [
65+
{ type: 'screenshot', required: true },
66+
];
67+
68+
const wrapper = mount(EvidenceSubmissionForm, {
69+
props: { step, evidenceRequirements: requirements },
70+
global: { stubs },
71+
});
72+
73+
const optionTexts = wrapper
74+
.findAll('.select-option')
75+
.map((o) => o.text().trim());
76+
77+
expect(optionTexts).toContain('screenshot');
78+
});
79+
});

0 commit comments

Comments
 (0)