Skip to content

Commit

Permalink
Merge pull request #168 from DEFRA/BAU-add-generic-viewmodel-for-answers
Browse files Browse the repository at this point in the history
BAU: lift viewModel into a generic AnswerModel fn
  • Loading branch information
hughfdjackson authored Jan 28, 2025
2 parents 0c5df9d + 8184ff7 commit b118f4b
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export class QuestionPageController extends GenericPageController {
heading: this.page.heading,
value: answer.value,
answer,
viewModelOptions: { validate: false },
...this.page.viewProps(req)
})
}
Expand All @@ -76,6 +77,7 @@ export class QuestionPageController extends GenericPageController {
heading: this.page.heading,
value: answer.value,
answer,
viewModelOptions: { validate: true },
errors,
errorMessages: Answer.errorMessages(errors),
...this.page.viewProps(req)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,8 @@ describe('QuestionPageController', () => {
heading: question,
nextPage: 'redirect_uri',
pageTitle: question,
value: undefined
value: undefined,
viewModelOptions: { validate: false }
})
)

Expand All @@ -491,6 +492,7 @@ describe('QuestionPageController', () => {
{ href: `#${questionKey}`, text: 'There is a problem' }
],
errors: { questionKey: { text: 'There is a problem' } },
viewModelOptions: { validate: true },
heading: question,
nextPage: 'test_next_page',
pageTitle: `Error: ${question}`,
Expand Down
11 changes: 11 additions & 0 deletions src/server/common/model/answer/answer-model.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@ export class AnswerModel {
throw new NotImplementedError()
}

/**
* @param {AnswerViewModelOptions} _options
*/
viewModel(_options) {
throw new NotImplementedError()
}

/**
* @param {unknown} _data
* @returns {unknown}
Expand All @@ -91,3 +98,7 @@ export class AnswerModel {
/**
* @typedef {{[key:string]: string | undefined}} RawPayload
*/

/**
* @typedef {{ validate: boolean }} AnswerViewModelOptions
*/
6 changes: 6 additions & 0 deletions src/server/common/model/answer/answer-model.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ describe('AnswerModel', () => {
expect(() => AnswerModel.fromState({})).toThrow(notImplementedError)
})

it('should throw NotImplementedError when viewModel is called', () => {
expect(() => answer.viewModel({ validate: true })).toThrow(
notImplementedError
)
})

it('should seal the object to prevent property additions or deletions', () => {
class AnswerModelBasic extends AnswerModel {
_extractFields(data) {
Expand Down
15 changes: 13 additions & 2 deletions src/server/common/model/answer/radio-button/radio-button.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { AnswerModel } from '../answer-model.js'
import { validateAnswerAgainstSchema } from '../validation.js'
import { NotImplementedError } from '../../../helpers/not-implemented-error.js'

/** @import {AnswerViewModelOptions} from '../answer-model.js' */

/* eslint-disable jsdoc/require-returns-check */

/**
Expand Down Expand Up @@ -93,7 +95,10 @@ export class RadioButtonAnswer extends AnswerModel {
})
}

get viewModel() {
/**
* @param {AnswerViewModelOptions} options
*/
viewModel({ validate }) {
const items = Object.entries(this.config.options).map(([key, value]) => ({
id: key,
value: key,
Expand All @@ -103,12 +108,18 @@ export class RadioButtonAnswer extends AnswerModel {
}
}))
items[0].id = this.config.payloadKey
return {

const model = {
name: this.config.payloadKey,
id: this.config.payloadKey,
fieldset: {},
value: this.value,
items
}

if (validate) {
model.errorMessage = this.validate().errors[this.config.payloadKey]
}
return model
}
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
{% from "govuk/components/radios/macro.njk" import govukRadios %}
{{ govukRadios(assign({}, answer.viewModel, { errorMessage: errors[answer.config.payloadKey]})) }}
{{ govukRadios(answer.viewModel(viewModelOptions)) }}
57 changes: 33 additions & 24 deletions src/server/common/model/answer/radio-button/radio-button.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,31 +141,40 @@ describe('RadioButton', () => {
})

describe('#RadioButton.viewModel', () => {
it('should return everything (except errors) to render in the template', () => {
const answer = new RadioButtonTest({ test_radio: 'value_1' })
expect(answer.viewModel).toEqual({
name: 'test_radio',
id: 'test_radio',
fieldset: {},
value: answer.value,
items: [
{
id: 'test_radio',
value: 'value_1',
text: 'test_label_1',
hint: {
text: undefined
}
},
{
id: 'value_2',
value: 'value_2',
text: 'test_label_2',
hint: {
text: 'test_hint_2'
}
const answer = new RadioButtonTest({ test_radio: 'invalid_answer' })
const defaultViewModel = {
name: 'test_radio',
id: 'test_radio',
fieldset: {},
value: answer.value,
items: [
{
id: 'test_radio',
value: 'value_1',
text: 'test_label_1',
hint: {
text: undefined
}
},
{
id: 'value_2',
value: 'value_2',
text: 'test_label_2',
hint: {
text: 'test_hint_2'
}
]
}
]
}

it('should return everything (except errors) to render in the template', () => {
expect(answer.viewModel({ validate: false })).toEqual(defaultViewModel)
})

it('should return everything (including errors) to render in the template', () => {
expect(answer.viewModel({ validate: true })).toEqual({
...defaultViewModel,
errorMessage: { text: 'Select an option' }
})
})
})
Expand Down

0 comments on commit b118f4b

Please sign in to comment.