diff --git a/config/questionnaires/bus_feedback.yaml b/config/questionnaires/bus_feedback.yaml index 7a0527f..e418238 100644 --- a/config/questionnaires/bus_feedback.yaml +++ b/config/questionnaires/bus_feedback.yaml @@ -35,6 +35,7 @@ sections: - key: bus_company label: "Bus-Unternehmen:" type: choice + widget: dropdown required: true options: - { key: boonk, label: "Boonk Reisen" } @@ -80,6 +81,7 @@ sections: - key: overall_impression label: "Gesamteindruck des Busses" type: choice + widget: dropdown required: true options: - { key: very_good, label: "Sehr gut" } @@ -91,6 +93,7 @@ sections: - key: vehicle_age label: "Alter des Fahrzeugs" type: choice + widget: dropdown required: true options: - { key: very_new, label: "Sehr neu" } @@ -102,6 +105,7 @@ sections: - key: seating_comfort label: "Sitzkomfort im Bus" type: choice + widget: dropdown required: true options: - { key: very_good, label: "Sehr gut" } @@ -113,6 +117,7 @@ sections: - key: driver_friendliness label: "Freundlichkeit der Fahrer" type: choice + widget: dropdown required: true options: - { key: very_friendly, label: "Sehr freundlich" } @@ -124,6 +129,7 @@ sections: - key: breaks label: "Pausenzeiten" type: choice + widget: dropdown required: true options: - { key: very_good, label: "Sehr gut" } @@ -135,6 +141,7 @@ sections: - key: temperature label: "Temperatur im Bus" type: choice + widget: dropdown required: true options: - { key: very_good, label: "Sehr gut" } @@ -146,6 +153,7 @@ sections: - key: guest_behaviour label: "Verhalten der Gäste im Bus" type: choice + widget: dropdown required: true options: - { key: very_good, label: "Sehr gut" } @@ -188,6 +196,7 @@ sections: - key: video label: "Video/DVD-Gerät" type: choice + widget: dropdown required: true options: - { key: working, label: "funktionstüchtig" } @@ -197,6 +206,7 @@ sections: - key: wc label: "WC" type: choice + widget: dropdown required: true options: - { key: working, label: "funktionstüchtig" } @@ -206,6 +216,7 @@ sections: - key: luggage label: "Gepäck" type: choice + widget: dropdown required: true options: - { key: trailer, label: "Anhänger" } @@ -216,6 +227,7 @@ sections: - key: smoking label: "Rauchen im Bus" type: choice + widget: dropdown required: true options: - { key: driver, label: "Busfahrer hat im Bus geraucht" } @@ -225,6 +237,7 @@ sections: - key: snow_chains label: "Schneeketten" type: choice + widget: dropdown required: true options: - { key: fitted, label: "Fahrer hat Ketten aufgezogen" } @@ -237,6 +250,7 @@ sections: - key: bus_damage label: "Entstanden während der Fahrt Schäden am Bus?" type: choice + widget: dropdown required: true options: - { key: none, label: "Keine Schäden" } diff --git a/config/questionnaires/teamer_feedback.yaml b/config/questionnaires/teamer_feedback.yaml index 60db841..714bab4 100644 --- a/config/questionnaires/teamer_feedback.yaml +++ b/config/questionnaires/teamer_feedback.yaml @@ -42,6 +42,7 @@ sections: - key: team_week_seminar label: "War das Wochenteam-Seminar hilfreich für dich? Warst du bzw. wurdest du gut vorbereitet?" type: choice + widget: dropdown required: true options: - { key: very_helpful, label: "Ja, die Schulung war sehr hilfreich und ich fühlte mich bestens vorbereitet." } @@ -84,6 +85,7 @@ sections: - key: house_manager_problem_handling label: "Wie ist deine Hausleitung mit Problemen vor Ort umgegangen?" type: choice + widget: dropdown required: true options: - { key: very_good, label: "Sehr gut – Probleme wurden schnell, fair und lösungsorientiert behandelt." } diff --git a/src/Form/QuestionnaireType.php b/src/Form/QuestionnaireType.php index 21d93fc..d5bc246 100644 --- a/src/Form/QuestionnaireType.php +++ b/src/Form/QuestionnaireType.php @@ -151,14 +151,20 @@ class QuestionnaireType extends AbstractType return match ($question->type) { QuestionType::TEXT => [TextType::class, $common], QuestionType::TEXTAREA => [AutosizeTextareaType::class, $common + ['min_rows' => 3]], - QuestionType::CHOICE => [ChoiceType::class, $common + $this->choices($question) + [ - 'expanded' => ChoiceWidget::RADIO === $question->widget, - // a radio list needs no "none" entry, a select needs an empty first line - 'placeholder' => ChoiceWidget::RADIO === $question->widget ? false : 'Bitte wählen', - ]], + QuestionType::CHOICE => [ChoiceType::class, $common + $this->choices($question) + (ChoiceWidget::RADIO === $question->widget ? [ + 'expanded' => true, + // a radio list needs no "none" entry + 'placeholder' => false, + 'block_prefix' => 'questionnaire_choice', + ] : [ + 'expanded' => false, + // a select needs an empty first line, or the first option would be preselected + 'placeholder' => 'Bitte wählen', + ])], QuestionType::MULTI => [ChoiceType::class, $common + $this->choices($question) + [ 'expanded' => true, 'multiple' => true, + 'block_prefix' => 'questionnaire_choice', ]], QuestionType::DATE => [DatepickerType::class, $common + [ 'input' => 'string', diff --git a/templates/forms.html.twig b/templates/forms.html.twig index f9676d5..1255704 100644 --- a/templates/forms.html.twig +++ b/templates/forms.html.twig @@ -207,6 +207,23 @@ {%- endblock questionnaire_section_row -%} +{# + # A questionnaire's radio or checkbox list: each answer a whole clickable row that lights up on + # hover and stays marked once chosen, so it is plain where to click. Questionnaire-only; the + # app's other choice lists keep choice_widget_expanded. + #} +{%- block questionnaire_choice_widget -%} +
+ {%- for child in form -%} + + {%- endfor -%} +
+{%- endblock questionnaire_choice_widget -%} + {# A 1-5 rating as a row of buttons, the radios themselves hidden. #} {%- block questionnaire_rating_widget -%}
diff --git a/tests/Twig/QuestionnaireTemplateTest.php b/tests/Twig/QuestionnaireTemplateTest.php index c067164..da9c0b0 100644 --- a/tests/Twig/QuestionnaireTemplateTest.php +++ b/tests/Twig/QuestionnaireTemplateTest.php @@ -59,6 +59,27 @@ class QuestionnaireTemplateTest extends KernelTestCase $this->assertSame([$this->definition()->title, $this->busDefinition()->title], $headings); } + /** + * Long scales are selects, starting empty; Ja/Nein stays radios, each answer a clickable row. + */ + public function testLongScalesAreSelectsAndYesNoAreClickableRows(): void + { + $crawler = $this->renderForm(); + + $seminar = $crawler->filter('[data-question-key="teamer_feedback.team_week_seminar"] select'); + $this->assertCount(1, $seminar); + $this->assertSame('', $seminar->filter('option')->first()->attr('value')); + $this->assertCount(1, $crawler->filter('[data-question-key="bus_feedback.wc"] select')); + + // the labels wrapping a radio: the whole row is the click target + $rows = $crawler + ->filter('[data-question-key="teamer_feedback.had_organisational_problems"] label') + ->reduce(static fn (Crawler $label): bool => 1 === $label->filter('input[type="radio"]')->count()) + ; + $this->assertCount(2, $rows); + $this->assertStringContainsString('hover:', $rows->first()->attr('class')); + } + public function testRatingIsFiveRadiosInARadioGroup(): void { $group = $this->renderForm()->filter('[data-question-key="teamer_feedback.office_briefing_rating"] [role="radiogroup"]');