Skip to content

Commit 5c7319b

Browse files
Merge pull request #1893 from nhsuk/character-count-function
[v10] Deprecate character count `maxwords` and add `countType` option
2 parents d8385a4 + 1d930a6 commit 5c7319b

9 files changed

Lines changed: 290 additions & 121 deletions

File tree

docs/configuration/javascript-api-reference.md

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,13 @@ false
3939

4040
Type: number
4141

42-
The maximum number of characters. If `maxwords` is provided, the `maxlength` option will be ignored.
42+
The maximum number of characters (or words if `countType` is set to `'words'`).
4343

44-
### maxwords
44+
### maxwords (deprecated)
4545

4646
Type: number
4747

48-
The maximum number of words. If `maxwords` is provided, the `maxlength` option will be ignored.
48+
The maximum number of words. Replaced by the `maxlength` and `countType: 'words'` options.
4949

5050
### threshold
5151

@@ -59,6 +59,18 @@ Default:
5959
0
6060
```
6161

62+
### countType
63+
64+
Type: string
65+
66+
The count type (`'length'` or `'words'`) used to count the text.
67+
68+
Default:
69+
70+
```json5
71+
'length'
72+
```
73+
6274
### textareaDescriptionClass
6375

6476
Type: string
@@ -215,7 +227,7 @@ Default:
215227

216228
Type: object
217229

218-
Message made available to assistive technologies, if none is already present in the HTML, to describe that the component accepts only a limited amount of content. The component will replace the `%{count}` placeholder with the value of the `maxlength` or `maxwords` option.
230+
Message made available to assistive technologies, if none is already present in the HTML, to describe that the component accepts only a limited amount of content. The component will replace the `%{count}` placeholder with the value of the `maxlength` option.
219231

220232
Default:
221233

packages/nhsuk-frontend-review/src/examples/translated.njk

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@
7474
text: "Peidiwch â chynnwys gwybodaeth bersonol, fel eich enw, dyddiad geni na rhif y GIG"
7575
},
7676
name: "with-translations-maxwords",
77-
maxwords: 10,
77+
maxlength: 10,
78+
countType: "words",
7879
textareaDescriptionText: "Gallwch ddefnyddio hyd at %{count} nod",
7980
wordsUnderLimitText: {
8081
one: "Mae gennych %{count} gair ar ôl",

packages/nhsuk-frontend/src/nhsuk/components/character-count/character-count.jsdom.test.mjs

Lines changed: 113 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ describe('Character count', () => {
3434

3535
jest.spyOn($textarea, 'addEventListener')
3636
jest.spyOn(window, 'addEventListener')
37+
jest.spyOn(console, 'warn').mockImplementation()
3738
}
3839

3940
beforeEach(() => {
@@ -162,6 +163,35 @@ describe('Character count', () => {
162163
expect(component.updateCount).toHaveBeenCalled()
163164
expect(component.updateIfValueChanged).toHaveBeenCalled()
164165
})
166+
167+
it('should handle deprecated params', () => {
168+
$textarea.value = 'Existing value'
169+
170+
const component = new CharacterCount($root)
171+
172+
expect(component.getCountMessage()).toBe(
173+
'You have 186 characters remaining'
174+
)
175+
176+
// Temporarily allow deprecated `text` parameter to update count
177+
// even though the textarea value has not been updated
178+
component.updateCount('')
179+
180+
// Temporarily allow deprecated `countType = 'characters'` parameter
181+
expect(component.formatCountMessage(200, 'characters')).toBe(
182+
'You have 200 characters remaining'
183+
)
184+
185+
// Temporarily allow deprecated `countType = 'length'` parameter
186+
expect(component.formatCountMessage(200, 'length')).toBe(
187+
'You have 200 characters remaining'
188+
)
189+
190+
// Temporarily allow deprecated `countType = 'words'` parameter
191+
expect(component.formatCountMessage(200, 'words')).toBe(
192+
'You have 200 words remaining'
193+
)
194+
})
165195
})
166196

167197
describe('Nunjucks configuration', () => {
@@ -170,18 +200,49 @@ describe('Character count', () => {
170200
expect(characterCount.config).toEqual({
171201
...CharacterCount.defaults,
172202
maxlength: 200,
173-
threshold: 0
203+
threshold: 0,
204+
countType: 'length'
174205
})
175206
})
176207

177-
it('configures `maxwords`', () => {
178-
initExample('with word count')
208+
it('configures `maxwords` (deprecated)', () => {
209+
initExample('with maxwords')
179210

180211
const characterCount = new CharacterCount($root)
181212
expect(characterCount.config).toEqual({
182213
...CharacterCount.defaults,
214+
maxlength: 150,
183215
maxwords: 150,
184-
threshold: 0
216+
threshold: 0,
217+
countType: 'words'
218+
})
219+
220+
expect(console.warn).toHaveBeenCalledWith(
221+
`${CharacterCount.moduleName}: Option \`maxwords\` is deprecated. Use \`maxlength\` with \`countType: "words"\` instead.`
222+
)
223+
})
224+
225+
it('configures `countType: "length"`', () => {
226+
initExample("with count type 'length'")
227+
228+
const characterCount = new CharacterCount($root)
229+
expect(characterCount.config).toEqual({
230+
...CharacterCount.defaults,
231+
maxlength: 200,
232+
threshold: 0,
233+
countType: 'length'
234+
})
235+
})
236+
237+
it('configures `countType: "words"`', () => {
238+
initExample("with count type 'words'")
239+
240+
const characterCount = new CharacterCount($root)
241+
expect(characterCount.config).toEqual({
242+
...CharacterCount.defaults,
243+
maxlength: 150,
244+
threshold: 0,
245+
countType: 'words'
185246
})
186247
})
187248

@@ -192,7 +253,8 @@ describe('Character count', () => {
192253
expect(characterCount.config).toEqual({
193254
...CharacterCount.defaults,
194255
maxlength: 112,
195-
threshold: 75
256+
threshold: 75,
257+
countType: 'length'
196258
})
197259
})
198260

@@ -215,7 +277,8 @@ describe('Character count', () => {
215277
expect(characterCount.config).toEqual({
216278
...CharacterCount.defaults,
217279
maxlength: 200,
218-
threshold: 0
280+
threshold: 0,
281+
countType: 'length'
219282
})
220283
})
221284
})
@@ -234,12 +297,10 @@ describe('Character count', () => {
234297
}
235298
})
236299

237-
expect(component.formatCountMessage(1, 'characters')).toBe(
238-
'Custom text. Count: 1'
239-
)
300+
expect(component.formatCountMessage(1)).toBe('Custom text. Count: 1')
240301

241302
// Other keys remain untouched
242-
expect(component.formatCountMessage(10, 'characters')).toBe(
303+
expect(component.formatCountMessage(10)).toBe(
243304
'You have 10 characters remaining'
244305
)
245306
})
@@ -252,9 +313,7 @@ describe('Character count', () => {
252313
}
253314
})
254315

255-
expect(component.formatCountMessage(0, 'characters')).toBe(
256-
'Custom text.'
257-
)
316+
expect(component.formatCountMessage(0)).toBe('Custom text.')
258317
})
259318

260319
it('uses specific translation keys when `maxwords` limit is reached', () => {
@@ -265,9 +324,7 @@ describe('Character count', () => {
265324
}
266325
})
267326

268-
expect(component.formatCountMessage(0, 'words')).toBe(
269-
'Different custom text.'
270-
)
327+
expect(component.formatCountMessage(0)).toBe('Different custom text.')
271328
})
272329

273330
it('uses existing textarea value for `maxlength` limit when initialised', () => {
@@ -337,7 +394,7 @@ describe('Character count', () => {
337394
maxwords: 20000
338395
})
339396

340-
expect(component.formatCountMessage(10000, 'words')).toBe(
397+
expect(component.formatCountMessage(10000)).toBe(
341398
'You have 10.000 words remaining'
342399
)
343400
})
@@ -349,7 +406,7 @@ describe('Character count', () => {
349406
maxwords: 20000
350407
})
351408

352-
expect(component.formatCountMessage(10000, 'words')).toBe(
409+
expect(component.formatCountMessage(10000)).toBe(
353410
'You have 10.000 words remaining'
354411
)
355412
})
@@ -366,12 +423,10 @@ describe('Character count', () => {
366423
maxlength: 100
367424
})
368425

369-
expect(component.formatCountMessage(1, 'characters')).toBe(
370-
'Custom text. Count: 1'
371-
)
426+
expect(component.formatCountMessage(1)).toBe('Custom text. Count: 1')
372427

373428
// Other keys remain untouched
374-
expect(component.formatCountMessage(10, 'characters')).toBe(
429+
expect(component.formatCountMessage(10)).toBe(
375430
'You have 10 characters remaining'
376431
)
377432
})
@@ -391,16 +446,14 @@ describe('Character count', () => {
391446
}
392447
})
393448

394-
expect(component.formatCountMessage(1, 'characters')).toBe(
395-
'Custom text. Count: 1'
396-
)
449+
expect(component.formatCountMessage(1)).toBe('Custom text. Count: 1')
397450

398451
// Other keys remain untouched
399-
expect(component.formatCountMessage(-10, 'characters')).toBe(
452+
expect(component.formatCountMessage(-10)).toBe(
400453
'You have 10 characters too many'
401454
)
402455

403-
expect(component.formatCountMessage(0, 'characters')).toBe(
456+
expect(component.formatCountMessage(0)).toBe(
404457
'You have 0 characters remaining'
405458
)
406459
})
@@ -409,7 +462,11 @@ describe('Character count', () => {
409462
})
410463

411464
describe('Character count: Format count message', () => {
412-
let /** @type {CharacterCount} */ componentWithMaxLength
465+
let /** @type {CharacterCount} */ component
466+
let /** @type {CharacterCount} */ componentWithCountTypeCharacters
467+
let /** @type {CharacterCount} */ componentWithCountTypeWords
468+
469+
// Deprecated `maxwords` option where `countType` is inferred
413470
let /** @type {CharacterCount} */ componentWithMaxWords
414471

415472
beforeEach(() => {
@@ -421,18 +478,30 @@ describe('Character count: Format count message', () => {
421478
<body class="nhsuk-frontend-supported">
422479
${components.render('character-count', example)}
423480
${components.render('character-count', example)}
481+
${components.render('character-count', example)}
482+
${components.render('character-count', example)}
424483
</body>
425484
`
426485

427486
const $roots = document.querySelectorAll(
428487
`[data-module="${CharacterCount.moduleName}"]`
429488
)
430489

431-
componentWithMaxLength = new CharacterCount($roots[0], {
490+
component = new CharacterCount($roots[0], {
432491
maxlength: 100
433492
})
434493

435-
componentWithMaxWords = new CharacterCount($roots[1], {
494+
componentWithCountTypeCharacters = new CharacterCount($roots[1], {
495+
maxlength: 100,
496+
countType: 'length'
497+
})
498+
499+
componentWithCountTypeWords = new CharacterCount($roots[2], {
500+
maxlength: 100,
501+
countType: 'words'
502+
})
503+
504+
componentWithMaxWords = new CharacterCount($roots[3], {
436505
maxwords: 100
437506
})
438507
})
@@ -446,53 +515,38 @@ describe('Character count: Format count message', () => {
446515
])(
447516
'outputs the expected translation for $number characters',
448517
({ number, expected }) => {
518+
expect(component.formatCountMessage(number)).toEqual(expected)
519+
449520
expect(
450-
componentWithMaxLength.formatCountMessage(number, 'characters')
521+
componentWithCountTypeCharacters.formatCountMessage(number)
451522
).toEqual(expected)
452523
}
453524
)
454525

455526
it.each([
456-
{
457-
number: 1,
458-
type: 'words',
459-
expected: 'You have 1 word remaining'
460-
},
461-
{
462-
number: 10,
463-
type: 'words',
464-
expected: 'You have 10 words remaining'
465-
},
466-
{
467-
number: -1,
468-
type: 'words',
469-
expected: 'You have 1 word too many'
470-
},
471-
{
472-
number: -10,
473-
type: 'words',
474-
expected: 'You have 10 words too many'
475-
},
476-
{
477-
number: 0,
478-
type: 'words',
479-
expected: 'You have 0 words remaining'
480-
}
527+
{ number: 1, expected: 'You have 1 word remaining' },
528+
{ number: 10, expected: 'You have 10 words remaining' },
529+
{ number: -1, expected: 'You have 1 word too many' },
530+
{ number: -10, expected: 'You have 10 words too many' },
531+
{ number: 0, expected: 'You have 0 words remaining' }
481532
])(
482533
'outputs the expected translation for $number words',
483534
({ number, expected }) => {
484-
expect(componentWithMaxWords.formatCountMessage(number, 'words')).toEqual(
535+
expect(componentWithCountTypeWords.formatCountMessage(number)).toEqual(
485536
expected
486537
)
538+
539+
// Deprecated `maxwords` option where `countType` is inferred
540+
expect(componentWithMaxWords.formatCountMessage(number)).toEqual(expected)
487541
}
488542
)
489543

490544
it('formats the number inserted in the message', () => {
491-
expect(componentWithMaxWords.formatCountMessage(10000, 'words')).toBe(
545+
expect(componentWithCountTypeWords.formatCountMessage(10000)).toBe(
492546
'You have 10,000 words remaining'
493547
)
494548

495-
expect(componentWithMaxWords.formatCountMessage(-10000, 'words')).toBe(
549+
expect(componentWithCountTypeWords.formatCountMessage(-10000)).toBe(
496550
'You have 10,000 words too many'
497551
)
498552
})

0 commit comments

Comments
 (0)