Follow @roschaefer's PR review suggestions

- disable button if form.reasonCategory is falsy
- extract valuesReasonCategoryOptions to global constant
- extract reasonCategoryOptions to created lifecycle hook to keep data()
clean and use map
- extract formSchema to components/utils and test
- remove validator from formSchema and validate based on "Deep Rules" https://github.com/yiminghe/async-validator#deep-rules
- Use v-model to update reasonCategory and reasonDescription
- default to error message in English from backend
- Update template slot to use new syntax
This commit is contained in:
mattwr18 2019-10-15 20:23:47 +02:00
parent 37bf37f39b
commit e8c6941142
8 changed files with 104 additions and 57 deletions

View File

@ -78,10 +78,7 @@ const validateReport = async (resolve, root, args, context, info) => {
}
})
if (existingReportedResource)
throw new Error(
`You have already reported the ${existingReportedResource.label}, please only report the same ${existingReportedResource.label} once`,
)
if (existingReportedResource) throw new Error(`${existingReportedResource.label}`)
return resolve(root, args, context, info)
}

View File

@ -8,9 +8,8 @@
<!-- eslint-disable-next-line vue/no-v-html -->
<p v-html="message" />
<ds-radio
:value="form.reasonCategory"
v-model="form.reasonCategory"
:schema="formSchema.reasonCategory"
:label="$t('report.reason.category.label')"
:options="form.reasonCategoryOptions"
@ -18,7 +17,7 @@
/>
<ds-input
class="reason-description"
:value="form.reasonDescription"
v-model="form.reasonDescription"
:schema="formSchema.reasonDescription"
:label="$t('report.reason.description.label')"
:placeholder="$t('report.reason.description.placeholder')"
@ -29,15 +28,16 @@
{{ form.reasonDescription.length }}/{{ formSchema.reasonDescription.max }}
</small>
<ds-space />
<template slot="footer">
<ds-button class="cancel" icon="close" @click="cancel">{{ $t('report.cancel') }}</ds-button>
<template #footer>
<ds-button class="cancel" icon="close" @click="cancel">
{{ $t('report.cancel') }}
</ds-button>
<ds-button
danger
class="confirm"
icon="exclamation-circle"
:disabled="failsValidations"
:disabled="!form.reasonCategory"
:loading="loading"
@click="confirm"
>
@ -50,6 +50,8 @@
<script>
import { SweetalertIcon } from 'vue-sweetalert-icons'
import { reportMutation } from '~/graphql/Moderation.js'
import { valuesReasonCategoryOptions } from '~/constants/modals.js'
import validReport from '~/components/utils/ReportModal'
export default {
name: 'ReportModal',
@ -62,58 +64,25 @@ export default {
id: { type: String, required: true },
},
data() {
// this list equals to enums in GraphQL schema file "backend/src/schema/types/type/REPORTED.gql"
let valuesReasonCategoryOptions = [
'discrimination_etc',
'pornographic_content_links',
'glorific_trivia_of_cruel_inhuman_acts',
'doxing',
'intentional_intimidation_stalking_persecution',
'advert_products_services_commercial',
'criminal_behavior_violation_german_law',
'other',
]
let reasonCategoryOptions = []
valuesReasonCategoryOptions.forEach(reasonCategory => {
reasonCategoryOptions.push({
label: this.$t('report.reason.category.options.' + reasonCategory),
value: reasonCategory,
})
})
return {
isOpen: true,
success: false,
loading: false,
failsValidations: true,
form: {
reasonCategory: null,
reasonCategoryOptions,
reasonCategoryOptions: [],
reasonDescription: '',
},
formSchema: {
reasonCategory: {
type: 'enum',
required: true,
validator: (rule, value, callback, source, options) => {
this.form.reasonCategory = value
this.failsValidations = !this.form.reasonCategory
callback()
},
},
reasonDescription: {
type: 'string',
min: 0,
max: 200,
validator: (rule, value, callback, source, options) => {
this.form.reasonDescription = value
callback()
},
},
},
}
},
created() {
this.form.reasonCategoryOptions = valuesReasonCategoryOptions.map(reasonCategory => {
return {
label: this.$t('report.reason.category.options.' + reasonCategory),
value: reasonCategory,
}
})
},
computed: {
title() {
return this.$t(`report.${this.type}.title`)
@ -122,6 +91,12 @@ export default {
const name = this.$filters.truncate(this.name, 30)
return this.$t(`report.${this.type}.message`, { name })
},
formSchema() {
const validReportSchema = validReport({ translate: this.$t })
return {
...validReportSchema.formSchema,
}
},
},
methods: {
async cancel() {
@ -134,7 +109,6 @@ export default {
},
async confirm() {
const { reasonCategory, reasonDescription } = this.form
this.loading = true
// TODO: Use the "modalData" structure introduced in "ConfirmModal" and refactor this here. Be aware that all the Jest tests have to be refactored as well !!!
// await this.modalData.buttons.confirm.callback()
@ -172,6 +146,8 @@ export default {
case 'GraphQL error: Comment':
this.$toast.error(this.$t('report.comment.error'))
break
default:
this.$toast.error(err.message)
}
this.loading = false
})

View File

@ -0,0 +1,25 @@
import { valuesReasonCategoryOptions } from '~/constants/modals.js'
export default function validReport({ translate }) {
return {
formSchema: {
reasonCategory: {
type: 'object',
required: true,
fields: {
value: {
type: 'enum',
enum: valuesReasonCategoryOptions,
required: true,
message: translate('report.reason.category.invalid'),
},
},
},
reasonDescription: {
type: 'string',
min: 0,
max: 200,
},
},
}
}

View File

@ -0,0 +1,36 @@
import validReport from './ReportModal'
import Schema from 'async-validator'
let translate
beforeEach(() => {
translate = jest.fn(() => 'Validation error')
})
describe('validReport', () => {
let validate = object => {
const { formSchema } = validReport({ translate })
const validator = new Schema(formSchema)
return validator.validate(object, { suppressWarning: true }).catch(({ errors }) => {
throw new Error(errors[0].message)
})
}
describe('reasonCategory', () => {
describe('invalid enum', () => {
it('rejects', async () => {
await expect(validate({ reasonCategory: { value: 'invalid_enum' } })).rejects.toThrow(
'Validation error',
)
})
})
describe('valid enum', () => {
it('resolves', async () => {
await expect(
validate({ reasonCategory: { value: 'discrimination_etc' } }),
).resolves.toBeUndefined()
})
})
})
})

View File

@ -0,0 +1,11 @@
// this list equals to enums in GraphQL schema file "backend/src/schema/types/type/REPORTED.gql"
export const valuesReasonCategoryOptions = [
'discrimination_etc',
'pornographic_content_links',
'glorific_trivia_of_cruel_inhuman_acts',
'doxing',
'intentional_intimidation_stalking_persecution',
'advert_products_services_commercial',
'criminal_behavior_violation_german_law',
'other',
]

View File

@ -507,7 +507,8 @@
"advert_products_services_commercial": "Bewerben von Produkten und Dienstleistungen mit kommerzieller Absicht.",
"criminal_behavior_violation_german_law": "Strafbares Verhalten bzw. Verstoß gegen deutsches Recht.",
"other": "Andere …"
}
},
"invalid": "Bitte wählen Sie eine gültige Kategorie aus"
},
"description": {
"label": "Bitte erkläre: Warum möchtest du dies melden?",

View File

@ -508,7 +508,8 @@
"advert_products_services_commercial": "Advertising products and services with commercial intent.",
"criminal_behavior_violation_german_law": "Criminal behavior or violation of German law.",
"other": "Other …"
}
},
"invalid": "Please select a valid category"
},
"description": {
"label": "Please explain: Why you like to report this?",

View File

@ -67,7 +67,7 @@
<template slot="reasonCategory" slot-scope="scope">
{{ $t('report.reason.category.options.' + scope.row.reasonCategory) }}
</template>
<!-- reasonCategory -->
<!-- reasonDescription -->
<template slot="reasonDescription" slot-scope="scope">
{{ scope.row.reasonDescription }}
</template>