Skip to content

Commit 5ef5ae4

Browse files
pkp/pkp-lib#10820 Display error summary at the top of the page with multiple forms (#494)
* pkp/pkp-lib#10820 Add FormErrorSummary component * pkp/pkp-lib#10820 Add showErrorFooter prop in Form and FormPage components * pkp/pkp-lib#10820 Use FormErrorSummary in UserInvitationDetailsFormStep component * pkp/pkp-lib#10820 Add locale keys for new error summary message
1 parent 64a811e commit 5ef5ae4

File tree

6 files changed

+106
-3
lines changed

6 files changed

+106
-3
lines changed

public/globals.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,10 @@ window.pkp = {
431431
'form.errorOne': 'Please correct one error.',
432432
'form.errors':
433433
'The form was not saved because {$count} error(s) were encountered. Please correct these errors and try again.',
434+
'form.errorSummaryOne':
435+
'1 error detected! Please correct the error below before proceeding.',
436+
'form.errorSummaryMany':
437+
'{$count} errors detected! Please correct the errors below before proceeding.',
434438
'form.multilingualLabel': '{$label} in {$localeName}',
435439
'form.multilingualProgress': '{$count}/{$total} languages completed',
436440
'form.saved': 'Saved',
@@ -454,8 +458,8 @@ window.pkp = {
454458
'grid.noItems': 'No Items',
455459
'grid.user.confirmLogInAs':
456460
'Log in as this user? All actions you perform will be attributed to this user.',
457-
'grid.user.currentUsers':'Current Users',
458-
'grid.action.mergeUser':'Merge User',
461+
'grid.user.currentUsers': 'Current Users',
462+
'grid.action.mergeUser': 'Merge User',
459463
'help.help': 'Help',
460464
'informationCenter.informationCenter': 'Information Center',
461465
'invitation.cancelInvite.actionName': 'Cancel Invite',

src/components/Form/Form.stories.js

+42
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import Form from './Form.vue';
2+
import FormErrorSummary from './FormErrorSummary.vue';
23
import {useContainerStateManager} from '@/composables/useContainerStateManager';
34
import FormBase from './mocks/form-base';
45
import FormMultilingual from './mocks/form-multilingual';
@@ -126,3 +127,44 @@ export const WithErrors = {
126127

127128
args: {},
128129
};
130+
131+
export const WithErrorSummary = {
132+
render: (args) => ({
133+
components: {PkpForm: Form, FormErrorSummary},
134+
setup() {
135+
const {get, set, components} = useContainerStateManager();
136+
set('example', {
137+
...FormUser,
138+
...args,
139+
errors: {
140+
email: ['Please provide a valid email address'],
141+
affiliation: {
142+
en: ['You must enter your affiliation in English.'],
143+
fr_CA: ['You must enter your affiliation in French.'],
144+
ar: ['You must enter your affiliation in Arabic.'],
145+
},
146+
'user-locales': ['You must select at least two options.'],
147+
bio: {
148+
en: [
149+
'Please provide a bio statement to accompany your publication.',
150+
],
151+
},
152+
country: ['Please select your country.'],
153+
'mailing-address': [
154+
'You must enter a mailing address where you can receive post.',
155+
],
156+
},
157+
});
158+
159+
return {args, get, set, components};
160+
},
161+
template: `
162+
<FormErrorSummary :errors="components.example.errors"/>
163+
<PkpForm v-bind="components.example" @set="set" />
164+
`,
165+
}),
166+
167+
args: {
168+
showErrorFooter: false,
169+
},
170+
};

src/components/Form/Form.vue

+8
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
:visible-locales="visibleLocales"
6262
:available-locales="availableLocales"
6363
:is-saving="isSaving"
64+
:show-error-footer="showErrorFooter"
6465
@change="fieldChanged"
6566
@page-submitted="nextPage"
6667
@previous-page="setCurrentPage(false)"
@@ -135,6 +136,13 @@ export default {
135136
* Async function, receiving data from form and returning {validationError, data} from useFetch
136137
*/
137138
customSubmit: Function,
139+
/** If the error summary is shown in the form's footer. */
140+
showErrorFooter: {
141+
type: Boolean,
142+
default() {
143+
return true;
144+
},
145+
},
138146
},
139147
emits: [
140148
/** When the form props need to be updated. The payload is an object with any keys that need to be modified. */
+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<template>
2+
<div
3+
v-if="errorsCount"
4+
class="inline-flex w-full gap-x-1 rounded border border-s-4 border-attention p-2"
5+
>
6+
<div class="leading-none">
7+
<Icon icon="Error" class="h-5 w-5" :inline="true" />
8+
</div>
9+
<span class="text-lg-normal">
10+
{{ message }}
11+
</span>
12+
</div>
13+
</template>
14+
15+
<script setup>
16+
import {computed} from 'vue';
17+
import Icon from '@/components/Icon/Icon.vue';
18+
import {useLocalize} from '@/composables/useLocalize';
19+
20+
const {t} = useLocalize();
21+
22+
const props = defineProps({
23+
errors: {
24+
type: Object,
25+
default: () => {},
26+
},
27+
});
28+
29+
const errorsCount = computed(() => Object.keys(props.errors).length);
30+
31+
const message = computed(() => {
32+
const count = errorsCount.value;
33+
if (count > 1) {
34+
return t('form.errorSummaryMany', {count});
35+
}
36+
return t('form.errorSummaryOne');
37+
});
38+
</script>

src/components/Form/FormPage.vue

+7-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
aria-live="polite"
2121
>
2222
<FormErrors
23-
v-if="Object.keys(errors).length"
23+
v-if="Object.keys(errors).length && showErrorFooter"
2424
:errors="errors"
2525
:fields="fields"
2626
@showField="showField"
@@ -108,6 +108,12 @@ export default {
108108
},
109109
},
110110
isSaving: Boolean,
111+
showErrorFooter: {
112+
type: Boolean,
113+
default() {
114+
return true;
115+
},
116+
},
111117
},
112118
data() {
113119
return {

src/pages/userInvitation/UserInvitationDetailsFormStep.vue

+5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
<template>
22
<div v-if="store.invitationPayload.userId === null">
3+
<div v-if="Object.keys(store.errors).length" class="p-4">
4+
<FormErrorSummary :errors="store.errors" />
5+
</div>
36
<PkpForm
47
v-bind="userForm"
58
class="userInvitation__stepForm"
9+
:show-error-footer="false"
610
@set="updateUserForm"
711
></PkpForm>
812
</div>
@@ -60,6 +64,7 @@ import {useTranslation} from '@/composables/useTranslation';
6064
import UserInvitationUserGroupsTable from './UserInvitationUserGroupsTable.vue';
6165
import {useUserInvitationPageStore} from './UserInvitationPageStore';
6266
import {useForm} from '@/composables/useForm';
67+
import FormErrorSummary from '@/components/Form/FormErrorSummary.vue';
6368
6469
/**
6570
* Update the payload by using form values on multilingual or not

0 commit comments

Comments
 (0)