diff --git a/packages/govuk-frontend/src/govuk/components/accordion/accordion.mjs b/packages/govuk-frontend/src/govuk/components/accordion/accordion.mjs index 1b8756ed9e..2a8a62d1ec 100644 --- a/packages/govuk-frontend/src/govuk/components/accordion/accordion.mjs +++ b/packages/govuk-frontend/src/govuk/components/accordion/accordion.mjs @@ -410,7 +410,7 @@ export class Accordion extends GOVUKFrontendComponent { ? this.i18n.t('hideSection') : this.i18n.t('showSection') - $showHideText.innerText = newButtonText + $showHideText.textContent = newButtonText $button.setAttribute('aria-expanded', `${expanded}`) // Update aria-label combining @@ -420,12 +420,12 @@ export class Accordion extends GOVUKFrontendComponent { `.${this.sectionHeadingTextClass}` ) if ($headingText instanceof HTMLElement) { - ariaLabelParts.push($headingText.innerText.trim()) + ariaLabelParts.push($headingText.textContent.trim()) } const $summary = $section.querySelector(`.${this.sectionSummaryClass}`) if ($summary instanceof HTMLElement) { - ariaLabelParts.push($summary.innerText.trim()) + ariaLabelParts.push($summary.textContent.trim()) } const ariaLabelMessage = expanded @@ -497,7 +497,7 @@ export class Accordion extends GOVUKFrontendComponent { : this.i18n.t('showAllSections') this.$showAllButton.setAttribute('aria-expanded', expanded.toString()) - this.$showAllText.innerText = newButtonText + this.$showAllText.textContent = newButtonText // Swap icon, toggle class if (expanded) { diff --git a/packages/govuk-frontend/src/govuk/components/accordion/accordion.puppeteer.test.js b/packages/govuk-frontend/src/govuk/components/accordion/accordion.puppeteer.test.js index 353d986a6e..b660c7934c 100644 --- a/packages/govuk-frontend/src/govuk/components/accordion/accordion.puppeteer.test.js +++ b/packages/govuk-frontend/src/govuk/components/accordion/accordion.puppeteer.test.js @@ -759,7 +759,7 @@ describe('/components/accordion', () => { }) ).rejects.toEqual({ name: 'ElementError', - message: 'Accordion: $module is not an instance of "HTMLElement"' + message: 'Accordion: $module is not an instance of HTMLElement' }) }) }) diff --git a/packages/govuk-frontend/src/govuk/components/button/button.puppeteer.test.js b/packages/govuk-frontend/src/govuk/components/button/button.puppeteer.test.js index 2e89385460..dcbec749fa 100644 --- a/packages/govuk-frontend/src/govuk/components/button/button.puppeteer.test.js +++ b/packages/govuk-frontend/src/govuk/components/button/button.puppeteer.test.js @@ -364,7 +364,7 @@ describe('/components/button', () => { }) ).rejects.toEqual({ name: 'ElementError', - message: 'Button: $module is not an instance of "HTMLElement"' + message: 'Button: $module is not an instance of HTMLElement' }) }) }) diff --git a/packages/govuk-frontend/src/govuk/components/character-count/character-count.jsdom.test.mjs b/packages/govuk-frontend/src/govuk/components/character-count/character-count.jsdom.test.mjs index 4f1d649ee5..4226992a45 100644 --- a/packages/govuk-frontend/src/govuk/components/character-count/character-count.jsdom.test.mjs +++ b/packages/govuk-frontend/src/govuk/components/character-count/character-count.jsdom.test.mjs @@ -1,19 +1,22 @@ +import { getExamples, renderComponent } from '@govuk-frontend/lib/components' + import { CharacterCount } from './character-count.mjs' describe('CharacterCount', () => { - let $container - let $textarea - - beforeAll(() => { - $container = document.createElement('div') - $textarea = document.createElement('textarea') - - // Component checks that GOV.UK Frontend is supported - document.body.classList.add('govuk-frontend-supported') + let html + + beforeAll(async () => { + const examples = await getExamples('character-count') + html = renderComponent( + 'character-count', + examples['to configure in JavaScript'] + ) + }) - // Component checks that required elements are present - $textarea.classList.add('govuk-js-character-count') - $container.appendChild($textarea) + beforeEach(async () => { + // Some tests add attributes to `document.body` so we need + // to reset it alongside the component's markup + document.body.outerHTML = `${html}` }) describe('formatCountMessage', () => { @@ -21,8 +24,8 @@ describe('CharacterCount', () => { let componentWithMaxLength let componentWithMaxWords - beforeAll(() => { - const $div = $container.cloneNode(true) + beforeEach(() => { + const $div = document.querySelector('[data-module]') componentWithMaxLength = new CharacterCount($div, { maxlength: 100 }) componentWithMaxWords = new CharacterCount($div, { maxwords: 100 }) }) @@ -87,7 +90,7 @@ describe('CharacterCount', () => { describe('i18n', () => { describe('JavaScript configuration', () => { it('overrides the default translation keys', () => { - const $div = $container.cloneNode(true) + const $div = document.querySelector('[data-module]') const component = new CharacterCount($div, { maxlength: 100, i18n: { @@ -108,7 +111,7 @@ describe('CharacterCount', () => { }) it('uses specific keys for when limit is reached', () => { - const $div = $container.cloneNode(true) + const $div = document.querySelector('[data-module]') const componentWithMaxLength = new CharacterCount($div, { maxlength: 100, i18n: { @@ -136,7 +139,7 @@ describe('CharacterCount', () => { describe('lang attribute configuration', () => { it('overrides the locale when set on the element', () => { - const $div = $container.cloneNode(true) + const $div = document.querySelector('[data-module]') $div.setAttribute('lang', 'de') const component = new CharacterCount($div, { maxwords: 20000 }) @@ -148,11 +151,9 @@ describe('CharacterCount', () => { }) it('overrides the locale when set on an ancestor', () => { - const $parent = document.createElement('div') - $parent.setAttribute('lang', 'de') + document.body.setAttribute('lang', 'de') - const $div = $container.cloneNode(true) - $parent.appendChild($div) + const $div = document.querySelector('[data-module]') const component = new CharacterCount($div, { maxwords: 20000 }) @@ -165,7 +166,7 @@ describe('CharacterCount', () => { describe('Data attribute configuration', () => { it('overrides the default translation keys', () => { - const $div = $container.cloneNode(true) + const $div = document.querySelector('[data-module]') $div.setAttribute( 'data-i18n.characters-under-limit.one', 'Custom text. Count: %{count}' @@ -187,7 +188,7 @@ describe('CharacterCount', () => { describe('precedence over JavaScript configuration', () => { it('overrides translation keys', () => { - const $div = $container.cloneNode(true) + const $div = document.querySelector('[data-module]') $div.setAttribute( 'data-i18n.characters-under-limit.one', 'Custom text. Count: %{count}' diff --git a/packages/govuk-frontend/src/govuk/components/character-count/character-count.mjs b/packages/govuk-frontend/src/govuk/components/character-count/character-count.mjs index 508c34af64..1dffe3acee 100644 --- a/packages/govuk-frontend/src/govuk/components/character-count/character-count.mjs +++ b/packages/govuk-frontend/src/govuk/components/character-count/character-count.mjs @@ -88,7 +88,11 @@ export class CharacterCount extends GOVUKFrontendComponent { $textarea instanceof HTMLInputElement ) ) { - return this + throw new ElementError($textarea, { + componentName: 'Character count', + identifier: '.govuk-js-character-count', + expectedType: 'HTMLTextareaElement or HTMLInputElement' + }) } // Read config set using dataset ('data-' values) @@ -133,18 +137,20 @@ export class CharacterCount extends GOVUKFrontendComponent { this.$module = $module this.$textarea = $textarea - const $textareaDescription = document.getElementById( - `${this.$textarea.id}-info` - ) + const textareaDescriptionId = `${this.$textarea.id}-info` + const $textareaDescription = document.getElementById(textareaDescriptionId) if (!$textareaDescription) { - return + throw new ElementError($textareaDescription, { + componentName: 'Character count', + identifier: `#${textareaDescriptionId}` + }) } // Inject a description for the textarea if none is present already // for when the component was rendered with no maxlength, maxwords // nor custom textareaDescriptionText - if ($textareaDescription.innerText.match(/^\s*$/)) { - $textareaDescription.innerText = this.i18n.t('textareaDescription', { + if ($textareaDescription.textContent.match(/^\s*$/)) { + $textareaDescription.textContent = this.i18n.t('textareaDescription', { count: this.maxLength }) } @@ -318,7 +324,7 @@ export class CharacterCount extends GOVUKFrontendComponent { } // Update message - this.$visibleCountMessage.innerText = this.getCountMessage() + this.$visibleCountMessage.textContent = this.getCountMessage() } /** @@ -336,7 +342,7 @@ export class CharacterCount extends GOVUKFrontendComponent { } // Update message - this.$screenReaderCountMessage.innerText = this.getCountMessage() + this.$screenReaderCountMessage.textContent = this.getCountMessage() } /** diff --git a/packages/govuk-frontend/src/govuk/components/character-count/character-count.puppeteer.test.js b/packages/govuk-frontend/src/govuk/components/character-count/character-count.puppeteer.test.js index c5b505aca3..3faec7fd2b 100644 --- a/packages/govuk-frontend/src/govuk/components/character-count/character-count.puppeteer.test.js +++ b/packages/govuk-frontend/src/govuk/components/character-count/character-count.puppeteer.test.js @@ -808,10 +808,54 @@ describe('Character count', () => { $module.outerHTML = `` } }) + ).rejects.toEqual({ + name: 'ElementError', + message: 'Character count: $module is not an instance of HTMLElement' + }) + }) + + it('throws when the textarea is missing', async () => { + await expect( + renderAndInitialise(page, 'character-count', { + params: examples.default, + beforeInitialisation($module) { + $module.querySelector('.govuk-js-character-count').remove() + } + }) + ).rejects.toEqual({ + name: 'ElementError', + message: 'Character count: .govuk-js-character-count not found' + }) + }) + + it('throws when the textarea is not the right type', async () => { + await expect( + renderAndInitialise(page, 'character-count', { + params: examples.default, + beforeInitialisation($module) { + // Replace with a tag that's neither an `` or `