Skip to content

Commit b8a559f

Browse files
committed
feat: improve handling of option limits
Signed-off-by: Christian Hartmann <chris-hartmann@gmx.de>
1 parent 91f55e3 commit b8a559f

4 files changed

Lines changed: 111 additions & 3 deletions

File tree

lib/Service/FormsService.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,11 @@ public function getQuestions(int $formId): array {
130130
}
131131
}
132132

133+
// Set `isRequired` if minimum options limit is set
134+
if ($question['type'] === Constants::ANSWER_TYPE_MULTIPLE && $question['extraSettings']['optionsLimitMin'] > 0) {
135+
$question['isRequired'] = true;
136+
}
137+
133138
$questionList[] = $question;
134139
}
135140
} catch (DoesNotExistException $e) {

src/components/Questions/Question.vue

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,9 @@
147147
v-html="computedDescription" />
148148
<!-- eslint-enable vue/no-v-html -->
149149
</div>
150+
<NcNoteCard v-if="hasInfo" :id="infoId" type="info">
151+
{{ infoMessage }}
152+
</NcNoteCard>
150153
<NcNoteCard v-if="hasError" :id="errorId" type="error">
151154
{{ errorMessage }}
152155
</NcNoteCard>
@@ -263,6 +266,11 @@ export default {
263266
type: String,
264267
default: null,
265268
},
269+
270+
infoMessage: {
271+
type: String,
272+
default: null,
273+
},
266274
},
267275
268276
emits: [
@@ -336,9 +344,17 @@ export default {
336344
return !!this.errorMessage
337345
},
338346
347+
hasInfo() {
348+
return !!this.infoMessage
349+
},
350+
339351
errorId() {
340352
return `q${this.index}_error`
341353
},
354+
355+
infoId() {
356+
return `q${this.index}_info`
357+
},
342358
},
343359
344360
// Ensure description is sized correctly on initial render

src/components/Questions/QuestionMultiple.vue

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
:contentValid="contentValid"
1212
:shiftDragHandle="shiftDragHandle"
1313
:errorMessage="errorMessage"
14+
:infoMessage="infoMessage"
1415
v-on="commonListeners">
1516
<template #actions>
1617
<NcActionCheckbox
@@ -76,6 +77,7 @@
7677
<NcCheckboxRadioSwitch
7778
v-for="answer in choices"
7879
:key="answer.id"
80+
:aria-describedby="hasInfo ? infoId : undefined"
7981
:aria-errormessage="hasError ? errorId : undefined"
8082
:aria-invalid="hasError ? 'true' : undefined"
8183
:modelValue="questionValues"
@@ -219,7 +221,7 @@ export default {
219221
},
220222
221223
mixins: [QuestionMixin, QuestionMultipleMixin],
222-
emits: ['update:values'],
224+
emits: ['update:values', 'update:isRequired'],
223225
224226
setup() {
225227
return {
@@ -292,6 +294,54 @@ export default {
292294
this.updateOptionsOrder(value, OptionType.Choice)
293295
},
294296
},
297+
298+
availableOptions() {
299+
return (
300+
this.choices.filter(({ text }) => text.trim() !== '').length
301+
+ (this.allowOtherAnswer ? 1 : 0)
302+
)
303+
},
304+
305+
infoMessage() {
306+
const min = this.extraSettings?.optionsLimitMin ?? 0
307+
const max = this.extraSettings?.optionsLimitMax ?? 0
308+
309+
if (!min && !max) {
310+
return null
311+
}
312+
313+
if (min && max) {
314+
if (min === max) {
315+
return n(
316+
'forms',
317+
'Choose exactly one option',
318+
'Choose exactly %n options',
319+
min,
320+
)
321+
}
322+
323+
return t('forms', 'Choose between {min} and {max} options', {
324+
min,
325+
max,
326+
})
327+
}
328+
329+
if (min) {
330+
return n(
331+
'forms',
332+
'Choose at least one option',
333+
'Choose at least %n options',
334+
min,
335+
)
336+
}
337+
338+
return n(
339+
'forms',
340+
'Choose at most one option',
341+
'Choose at most %n options',
342+
max,
343+
)
344+
},
295345
},
296346
297347
watch: {
@@ -316,7 +366,7 @@ export default {
316366
this.errorMessage = n(
317367
'forms',
318368
'You must choose at most one option',
319-
'You must choose a maximum of %n options',
369+
'You must choose at most %n options',
320370
max,
321371
)
322372
return false
@@ -386,6 +436,19 @@ export default {
386436
// For unique (radio) options we cannot set limits, also if null is passed then we need to remove the limit
387437
this.onExtraSettingsChange({ optionsLimitMax: undefined })
388438
} else if (max) {
439+
if (max > this.availableOptions) {
440+
showError(
441+
t(
442+
'forms',
443+
'Upper options limit must not exceed the number of available options',
444+
),
445+
)
446+
this.onExtraSettingsChange({
447+
optionsLimitMax: this.availableOptions || undefined,
448+
})
449+
return
450+
}
451+
389452
if ((this.extraSettings.optionsLimitMin ?? 0) > max) {
390453
showError(
391454
t(
@@ -410,6 +473,19 @@ export default {
410473
if (this.isUnique || min === null) {
411474
this.onExtraSettingsChange({ optionsLimitMin: undefined })
412475
} else if (min) {
476+
if (min > this.availableOptions - 1) {
477+
showError(
478+
t(
479+
'forms',
480+
'Lower options limit must be smaller than the number of available options',
481+
),
482+
)
483+
this.onExtraSettingsChange({
484+
optionsLimitMin: this.availableOptions - 1 || undefined,
485+
})
486+
return
487+
}
488+
413489
if (
414490
this.extraSettings.optionsLimitMax
415491
&& min > this.extraSettings.optionsLimitMax
@@ -423,6 +499,9 @@ export default {
423499
return
424500
}
425501
this.onExtraSettingsChange({ optionsLimitMin: min })
502+
if (min > 0) {
503+
this.$emit('update:isRequired', true)
504+
}
426505
}
427506
},
428507

src/mixins/QuestionMixin.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,8 +217,16 @@ export default {
217217
return !!this.errorMessage
218218
},
219219

220+
hasInfo() {
221+
return !!this.infoMessage
222+
},
223+
220224
errorId() {
221-
return 'q' + this.index + '_error'
225+
return `q${this.index}_error`
226+
},
227+
228+
infoId() {
229+
return `q${this.index}_info`
222230
},
223231

224232
/**

0 commit comments

Comments
 (0)