From 092e0659643e58d91086d2149a29b52648f38e85 Mon Sep 17 00:00:00 2001 From: Ron Lucke <lucke@elan-ev.de> Date: Wed, 14 Feb 2024 07:27:34 +0000 Subject: [PATCH] fix #2768 Closes #2768 Merge request studip/studip!2465 --- resources/vue/components/StudipDialog.vue | 6 ++++- .../vue/components/StudipWizardDialog.vue | 24 ++++++++++++------- .../CoursewareStructuralElementDialogAdd.vue | 23 +++++++++++------- .../CoursewareStructuralElementDialogCopy.vue | 4 ++-- .../tasks/CoursewareTasksDialogDistribute.vue | 15 +++++++----- .../unit/CoursewareShelfDialogAdd.vue | 21 ++++++++-------- .../unit/CoursewareShelfDialogCopy.vue | 4 ++-- 7 files changed, 58 insertions(+), 39 deletions(-) diff --git a/resources/vue/components/StudipDialog.vue b/resources/vue/components/StudipDialog.vue index 8d885181227..f14dd371e57 100644 --- a/resources/vue/components/StudipDialog.vue +++ b/resources/vue/components/StudipDialog.vue @@ -1,6 +1,6 @@ <template> <MountingPortal mountTo="body" append> - <focus-trap v-model="trap" :initial-focus="() => $refs.buttonB"> + <focus-trap v-model="trap" :initial-focus="() => defaultFocus ? $refs.buttonB : null"> <div class="studip-dialog" @keydown.esc="closeDialog"> <transition name="dialog-fade"> <div class="studip-dialog-backdrop"> @@ -139,6 +139,10 @@ export default { question: String, alert: String, message: String, + defaultFocus: { + type: Boolean, + default: true + } }, data() { const dialogId = uuid++; diff --git a/resources/vue/components/StudipWizardDialog.vue b/resources/vue/components/StudipWizardDialog.vue index 5f0339bd7ce..e75c1cafac3 100644 --- a/resources/vue/components/StudipWizardDialog.vue +++ b/resources/vue/components/StudipWizardDialog.vue @@ -8,6 +8,7 @@ :confirmDisabled="!showConfirm" :closeText="closeText" :closeClass="closeClass" + :defaultFocus="false" @close="$emit('close')" @confirm="confirm" > @@ -22,7 +23,7 @@ <span>{{ $gettext('Bitte geben Sie die folgenden Informationen an:') }}</span> <ul> <li v-for="(requirement, index) in requirements" :key="requirement.slot.name + '_' + index"> - <button @click="selectSlot(requirement.slot.id)"> + <button @click="selectSlot(requirement.slot.id, requirement.target)"> <studip-icon :shape="requirement.slot.icon" :size="16" @@ -54,7 +55,7 @@ :aria-selected="activeId === progress.id" :aria-controls="progress.name" :tabindex="0" - @click="selectSlot(progress.id)" + @click="selectSlot(progress.id, progress.target)" @keydown.right="nextContent" @keydown.left="prevContent" > @@ -206,9 +207,7 @@ export default { return; } else { this.activeId = this.activeId - 1; - this.$nextTick(() => { - this.$refs.tabs[this.activeId - 1].focus(); - }); + this.selectSlot(this.activeId , this.activeSlot.target); } }, nextContent() { @@ -216,13 +215,17 @@ export default { return; } else { this.activeId = this.activeId + 1; - this.$nextTick(() => { - this.$refs.tabs[this.activeId - 1].focus(); - }); + this.selectSlot(this.activeId , this.activeSlot.target); } }, - selectSlot(id) { + selectSlot(id, target = null) { this.activeId = id; + if (target) { + this.$nextTick() + .then(() => { + document.getElementsByName(target)[0].focus(); + }); + } }, isValid(id) { const slot = this.slots.find( slot => slot.id === id); @@ -243,6 +246,9 @@ export default { this.$emit('confirm'); } }, + mounted() { + this.selectSlot(this.activeId , this.activeSlot.target); + }, watch: { activeId(newVal) { if (this.visitedIds.indexOf(newVal) === -1) { diff --git a/resources/vue/components/courseware/structural-element/CoursewareStructuralElementDialogAdd.vue b/resources/vue/components/courseware/structural-element/CoursewareStructuralElementDialogAdd.vue index 3d2e7e74f1a..c34e45d7a1f 100644 --- a/resources/vue/components/courseware/structural-element/CoursewareStructuralElementDialogAdd.vue +++ b/resources/vue/components/courseware/structural-element/CoursewareStructuralElementDialogAdd.vue @@ -13,7 +13,7 @@ <form class="default" @submit.prevent=""> <label> {{ $gettext('Position der neuen Seite') }} - <select v-model="pageParent"> + <select v-model="pageParent" name="relativePosition"> <option v-if="!isRoot && canEditParent" value="sibling"> {{ $gettext('Neben der aktuellen Seite') }} </option> @@ -23,11 +23,11 @@ <label> <span>{{ text.title }}</span> <span aria-hidden="true" class="wizard-required">*</span> - <input type="text" v-model="title" required /> + <input type="text" v-model="title" name="title" required /> </label> <label> <span>{{ $gettext('Beschreibung') }}</span> - <textarea v-model="description" required /> + <textarea v-model="description" name="description" required /> </label> </form> </template> @@ -35,7 +35,7 @@ <form v-if="hasTemplates" class="default" @submit.prevent=""> <label> {{ $gettext('Art der Vorlage') }} - <select v-model="templatePurpose"> + <select v-model="templatePurpose" name="templatePurpose"> <option value="content">{{ $gettext('Inhalt') }}</option> <option value="oer">{{ $gettext('OER-Material') }}</option> <option value="portfolio">{{ $gettext('ePortfolio') }}</option> @@ -46,7 +46,7 @@ </label> <label> <span>{{ $gettext('Vorlage') }}</span> - <select v-model="selectedTemplate"> + <select v-model="selectedTemplate" name="template"> <option :value="null">{{ $gettext('ohne Vorlage') }}</option> <option v-for="template in selectableTemplates" :key="template.id" :value="template"> {{ template.attributes.name }} @@ -70,6 +70,7 @@ ref="upload_image" type="file" accept="image/*" + name="image" @change="checkUploadFile" /> <courseware-companion-box @@ -81,7 +82,7 @@ </label> <label> {{ $gettext('Farbe') }} - <studip-select v-model="color" :options="colors" :reduce="(color) => color.class" label="class"> + <studip-select v-model="color" :options="colors" :reduce="(color) => color.class" label="class" name="color"> <template #open-indicator="selectAttributes"> <span v-bind="selectAttributes"><studip-icon shape="arr_1down" :size="10" /></span> </template> @@ -104,7 +105,7 @@ <form class="default" @submit.prevent=""> <label> {{ $gettext('Art des Lernmaterials') }} - <select v-model="purpose"> + <select v-model="purpose" name="purpose"> <option value="content">{{ $gettext('Inhalt') }}</option> <option value="oer">{{ $gettext('OER-Material') }}</option> <option value="portfolio">{{ $gettext('ePortfolio') }}</option> @@ -177,6 +178,7 @@ export default { name: 'basic', title: this.$gettext('Grundeinstellungen'), icon: 'courseware', + target: 'title', description: this.$gettext( 'Wählen Sie einen kurzen, prägnanten Titel und beschreiben Sie in einigen Worten den Inhalt der Seite.' ), @@ -187,6 +189,7 @@ export default { name: 'template', title: this.$gettext('Vorlage'), icon: 'content2', + target: 'templatePurpose', description: this.$gettext('Vorlagen enthalten Abschnitte und Blöcke, die bereits für bestimmte Zwecke angeordent sind. Beim anlegen der Seite, wird diese mit Abschnitten und Blöcken befüllt.'), }, { @@ -195,6 +198,7 @@ export default { name: 'layout', title: this.$gettext('Erscheinung'), icon: 'picture', + target: 'image', description: this.$gettext( 'Ein Vorschaubild motiviert Lernende die Seite zu erkunden. Die Kombination aus Bild und Farbe erleichtert das wiederfinden der Seite in einem Inhaltsverzeichnisblock.' ), @@ -205,6 +209,7 @@ export default { name: 'advanced', title: this.$gettext('Zusatzangaben'), icon: 'info-list', + target: 'purpose', description: this.$gettext( 'Hier können Sie detaillierte Angaben zur Seite eintragen. Diese sind besonders interessant wenn die Seite als OER geteilt wird.' ), @@ -278,7 +283,7 @@ export default { this.difficulty_end = ''; this.templatePurpose = 'content'; this.selectedTemplate = null; - this.requirements.push({ slot: this.wizardSlots[0], text: this.text.title }); + this.requirements.push({ slot: this.wizardSlots[0], text: this.text.title, target: 'title' }); }, closeAddDialog() { this.showElementAddDialog(false); @@ -372,7 +377,7 @@ export default { const slot = this.wizardSlots[0]; if (newTitle === '') { slot.valid = false; - this.requirements.push({ slot: slot, text: this.text.title }); + this.requirements.push({ slot: slot, text: this.text.title, target: 'title' }); } else { slot.valid = true; } diff --git a/resources/vue/components/courseware/structural-element/CoursewareStructuralElementDialogCopy.vue b/resources/vue/components/courseware/structural-element/CoursewareStructuralElementDialogCopy.vue index 82539ece70f..8d0406cfc49 100644 --- a/resources/vue/components/courseware/structural-element/CoursewareStructuralElementDialogCopy.vue +++ b/resources/vue/components/courseware/structural-element/CoursewareStructuralElementDialogCopy.vue @@ -137,7 +137,7 @@ <form v-if="selectedUnit" class="default" @submit.prevent=""> <label> {{$gettext('Titel')}} - <input type="text" v-model="modifiedTitle" required /> + <input type="text" v-model="modifiedTitle" name="title" required /> </label> <label> {{$gettext('Farbe')}} @@ -207,7 +207,7 @@ export default { description: this.$gettext('Wählen Sie das Lernmaterial aus, in dem sich der zu kopierende Lerninhalt befindet.') }, { id: 3, valid: false, name: 'element', title: this.$gettext('Seite'), icon: 'content2', description: this.$gettext('Wählen Sie die zu kopierende Seite aus. Um Unterseiten anzuzeigen, klicken Sie auf den Seitennamen. Mit einem weiteren Klick werden die Unterseiten wieder zugeklappt.') }, - { id: 4, valid: true, name: 'edit', title: this.$gettext('Anpassen'), icon: 'edit', + { id: 4, valid: true, name: 'edit', title: this.$gettext('Anpassen'), icon: 'edit', target: 'title', description: this.$gettext('Sie können hier die Daten der zu kopierenden Seite anpassen. Eine Anpassung ist optional, Sie können die Seite auch unverändert kopieren.') }, ], source: '', diff --git a/resources/vue/components/courseware/tasks/CoursewareTasksDialogDistribute.vue b/resources/vue/components/courseware/tasks/CoursewareTasksDialogDistribute.vue index e88bd04da57..1e076c752fa 100644 --- a/resources/vue/components/courseware/tasks/CoursewareTasksDialogDistribute.vue +++ b/resources/vue/components/courseware/tasks/CoursewareTasksDialogDistribute.vue @@ -60,7 +60,7 @@ <label> <span>{{ $gettext('Aufgabentitel') }}</span> <span aria-hidden="true" class="wizard-required">*</span> - <input type="text" v-model="taskTitle" required /> + <input type="text" v-model="taskTitle" name="taskTitle" required /> </label> <label> <span>{{ $gettext('Startdatum') }}</span> @@ -70,7 +70,7 @@ <label> <span>{{ $gettext('Abgabefrist') }}</span> <span aria-hidden="true" class="wizard-required">*</span> - <input type="date" v-model="endDate" :min="startDate" required /> + <input type="date" v-model="endDate" name="endDate" :min="startDate" required /> </label> <label> {{ $gettext('Inhalte ergänzen') }} @@ -287,6 +287,7 @@ export default { name: 'tasksettings', title: this.$gettext('Aufgabeneinstellungen'), icon: 'settings', + target: 'taskTitle', description: this.$gettext( 'Wählen Sie hier die Einstellungen der Aufgabe. Es muss ein Aufgabentitel und eine Abgabenfrist gesetzt werden.' ), @@ -600,8 +601,6 @@ export default { } else { this.wizardSlots[2].valid = false; } - - return this.wizardSlots[2].valid; }, validate() { this.requirements = []; @@ -611,9 +610,13 @@ export default { if (!this.selectedTaskIsTask) { this.requirements.push({ slot: this.wizardSlots[1], text: this.$gettext('Aufgabenvorlage') }); } - if (!this.validateTaskSettings()) { - this.requirements.push({ slot: this.wizardSlots[2], text: this.$gettext('Aufgabeneinstellungen') }); + if (this.taskTitle === '') { + this.requirements.push({ slot: this.wizardSlots[2], text: this.$gettext('Aufgabentitel'), target: 'taskTitle' }); + } + if (this.endDate === '') { + this.requirements.push({ slot: this.wizardSlots[2], text: this.$gettext('Abgabefrist'), target: 'endDate' }); } + this.validateTaskSettings(); if (this.selectedTargetUnit === null) { this.requirements.push({ slot: this.wizardSlots[3], text: this.$gettext(' Ziel-Lernmaterial') }); } diff --git a/resources/vue/components/courseware/unit/CoursewareShelfDialogAdd.vue b/resources/vue/components/courseware/unit/CoursewareShelfDialogAdd.vue index 43163ecf8d8..a55d1e0555c 100644 --- a/resources/vue/components/courseware/unit/CoursewareShelfDialogAdd.vue +++ b/resources/vue/components/courseware/unit/CoursewareShelfDialogAdd.vue @@ -13,11 +13,11 @@ <form class="default" @submit.prevent=""> <label> <span>{{ text.title }}</span><span aria-hidden="true" class="wizard-required">*</span> - <input type="text" v-model="addWizardData.title" required/> + <input type="text" v-model="addWizardData.title" name="title" required/> </label> <label> <span>{{ text.description }}</span><span aria-hidden="true" class="wizard-required">*</span> - <textarea v-model="addWizardData.description" required/> + <textarea v-model="addWizardData.description" name="description" required/> </label> </form> </template> @@ -26,7 +26,7 @@ <label> {{ $gettext('Bild hochladen') }} <br> - <input class="cw-file-input" ref="upload_image" type="file" accept="image/*" @change="checkUploadFile"/> + <input class="cw-file-input" ref="upload_image" type="file" accept="image/*" name="image" @change="checkUploadFile"/> <CoursewareCompanionBox v-if="uploadFileError" :msgCompanion="uploadFileError" @@ -57,6 +57,7 @@ :options="colors" :reduce="(color) => color.class" label="class" + name="color" > <template #open-indicator="selectAttributes"> <span v-bind="selectAttributes" @@ -78,7 +79,7 @@ </label> <label> <span>{{ $gettext('Titelseite') }}</span> - <select v-model="addWizardData.rootLayout"> + <select v-model="addWizardData.rootLayout" name="layout"> <option value="default">{{ $gettext('Automatisch') }}</option> <option value="toc">{{ $gettext('Automatisch mit Inhaltsverzeichnis') }}</option> <option value="classic">{{ $gettext('Frei bearbeitbar') }}</option> @@ -91,7 +92,7 @@ <form class="default" @submit.prevent=""> <label> {{ $gettext('Art des Lernmaterials') }} - <select v-model="addWizardData.purpose"> + <select v-model="addWizardData.purpose" name="purpose"> <option value="content">{{ $gettext('Inhalt') }}</option> <option value="oer">{{ $gettext('OER-Material') }}</option> <option value="portfolio">{{ $gettext('ePortfolio') }}</option> @@ -164,11 +165,11 @@ export default { data() { return { wizardSlots: [ - { id: 1, valid: false, name: 'basic', title: this.$gettext('Grundeinstellungen'), icon: 'courseware', + { id: 1, valid: false, name: 'basic', title: this.$gettext('Grundeinstellungen'), icon: 'courseware', target: 'title', description: this.$gettext('Wählen Sie einen kurzen, prägnanten Titel und beschreiben Sie in einigen Worten den Inhalt des Lernmaterials. Eine Beschreibung erleichtert Lernenden die Auswahl des Lernmaterials.') }, - { id: 2, valid: true, name: 'layout', title: this.$gettext('Darstellung'), icon: 'picture', + { id: 2, valid: true, name: 'layout', title: this.$gettext('Darstellung'), icon: 'picture', target: 'image', description: this.$gettext('Ein Vorschaubild motiviert Lernende das Lernmaterial zu erkunden. Die Kombination aus Bild und Farbe erleichtert das wiederfinden des Lernmaterials in der Übersicht.') }, - { id: 3, valid: true, name: 'advanced', title: this.$gettext('Zusatzangaben'), icon: 'info-list', + { id: 3, valid: true, name: 'advanced', title: this.$gettext('Zusatzangaben'), icon: 'info-list', target: 'purpose', description: this.$gettext('Hier können Sie detaillierte Angaben zum Lernmaterial eintragen. Diese sind besonders interessant wenn das Lernmaterial als OER geteilt wird.') } ], text: { @@ -321,11 +322,11 @@ export default { } if (newData.title === '' ) { slot.valid = false; - this.requirements.push({slot: slot, text: this.text.title }); + this.requirements.push({slot: slot, text: this.text.title, target: 'title' }); } if (newData.description === '') { slot.valid = false; - this.requirements.push({slot: slot, text: this.text.description }); + this.requirements.push({slot: slot, text: this.text.description, target: 'description' }); } }, deep: true diff --git a/resources/vue/components/courseware/unit/CoursewareShelfDialogCopy.vue b/resources/vue/components/courseware/unit/CoursewareShelfDialogCopy.vue index a2d1dec7d2a..ea475e75db8 100644 --- a/resources/vue/components/courseware/unit/CoursewareShelfDialogCopy.vue +++ b/resources/vue/components/courseware/unit/CoursewareShelfDialogCopy.vue @@ -123,7 +123,7 @@ <form v-if="selectedUnit" class="default" @submit.prevent=""> <label> <span>{{$gettext('Titel')}}</span><span aria-hidden="true" class="wizard-required">*</span> - <input type="text" v-model="modifiedTitle" :placeholder="selectedUnitTitle" required /> + <input type="text" v-model="modifiedTitle" :placeholder="selectedUnitTitle" name="title" required /> </label> <label> {{$gettext('Farbe')}} @@ -189,7 +189,7 @@ export default { description: this.$gettext('Wählen Sie hier den Ort in Stud.IP aus, an dem sich das zu kopierende Lernmaterial befindet.') }, { id: 2, valid: false, name: 'unit', title: this.$gettext('Lernmaterial'), icon: 'courseware', description: this.$gettext('Wählen Sie hier das gewünschte Lernmaterial aus der Liste aus. Eine Auswahl wird durch einen grauen Hintergrund und einen Kontrollhaken angezeigt.') }, - { id: 3, valid: true, name: 'edit', title: this.$gettext('Anpassen'), icon: 'edit', + { id: 3, valid: true, name: 'edit', title: this.$gettext('Anpassen'), icon: 'edit', target: 'title', description: this.$gettext('Sie können hier die Daten des zu kopierenden Lernmaterials anpassen. Eine Anpassung ist optional, Sie können das Lernmaterial auch unverändert kopieren.') }, ], source: '', -- GitLab