Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Label <button> with aria-labelledby (label + itself) #5652

Open
wants to merge 1 commit into
base: file-dropzone-design
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,13 @@ export class FileUpload extends ConfigurableComponent {
locale: closestAttributeValue(this.$root, 'lang')
})

this.$label = this.findLabel()
const $label = this.findLabel()
$label.setAttribute('for', `${this.id}-input`)
// Add an ID to the label if it doesn't have one already
// so it can be referenced by `aria-labelledby`
if (!$label.id) {
$label.id = `${this.id}-label`
}

// we need to copy the 'id' of the root element
// to the new button replacement element
Expand All @@ -72,10 +78,6 @@ export class FileUpload extends ConfigurableComponent {
const $wrapper = document.createElement('div')
$wrapper.className = 'govuk-file-upload-wrapper'

const commaSpan = document.createElement('span')
commaSpan.className = 'govuk-visually-hidden'
commaSpan.innerText = ', '

// Create the file selection button
const $button = document.createElement('button')
$button.classList.add('govuk-file-upload__button')
Expand All @@ -93,11 +95,16 @@ export class FileUpload extends ConfigurableComponent {
const $status = document.createElement('span')
$status.className = 'govuk-body govuk-file-upload__status'
$status.innerText = this.i18n.t('filesSelectedDefault')
$status.setAttribute('aria-hidden', 'true')
$status.classList.add('govuk-file-upload__status--empty')

$button.appendChild($status)
$button.appendChild(commaSpan.cloneNode(true))

const commaSpan = document.createElement('span')
commaSpan.className = 'govuk-visually-hidden'
commaSpan.innerText = ', '
commaSpan.id = `${this.id}-comma`

$button.appendChild(commaSpan)

const containerSpan = document.createElement('span')
containerSpan.className = 'govuk-file-upload__pseudo-button-container'
Expand All @@ -106,10 +113,8 @@ export class FileUpload extends ConfigurableComponent {
buttonSpan.className =
'govuk-button govuk-button--secondary govuk-file-upload__pseudo-button'
buttonSpan.innerText = this.i18n.t('selectFilesButton')
buttonSpan.setAttribute('aria-hidden', 'true')

containerSpan.appendChild(buttonSpan)
containerSpan.appendChild(commaSpan.cloneNode(true))

const instructionSpan = document.createElement('span')
instructionSpan.className = 'govuk-body govuk-file-upload__instruction'
Expand All @@ -119,8 +124,8 @@ export class FileUpload extends ConfigurableComponent {

$button.appendChild(containerSpan)
$button.setAttribute(
'aria-label',
`${this.$label.innerText}, ${this.i18n.t('selectFilesButton')} ${this.i18n.t('instruction')}, ${$status.innerText}`
'aria-labelledby',
`${$label.id} ${commaSpan.id} ${$button.id}`
)
$button.addEventListener('click', this.onClick.bind(this))

Expand All @@ -144,7 +149,7 @@ export class FileUpload extends ConfigurableComponent {
// Bind change event to the underlying input
this.$root.addEventListener('change', this.onChange.bind(this))

// Syncronise the `disabled` state between the button and underlying input
// Synchronise the `disabled` state between the button and underlying input
this.updateDisabledState()
this.observeDisabledState()

Expand Down Expand Up @@ -271,11 +276,6 @@ export class FileUpload extends ConfigurableComponent {

this.$status.classList.remove('govuk-file-upload__status--empty')
}

this.$button.setAttribute(
'aria-label',
`${this.$label.innerText}, ${this.i18n.t('selectFilesButton')} ${this.i18n.t('instruction')}, ${this.$status.innerText}`
)
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
/* eslint-disable no-new */

const { render } = require('@govuk-frontend/helpers/puppeteer')
const {
render,
getAccessibleName
} = require('@govuk-frontend/helpers/puppeteer')
const { getExamples } = require('@govuk-frontend/lib/components')

const inputSelector = '.govuk-file-upload'
Expand Down Expand Up @@ -104,15 +107,8 @@ describe('/components/file-upload', () => {
(el) => el.innerHTML.trim()
)

const buttonAriaText = await page.$eval(buttonSelector, (el) =>
el.getAttribute('aria-label')
)

expect(buttonElementText).toBe('Choose file')
expect(statusElementText).toBe('No file chosen')
expect(buttonAriaText).toBe(
'Upload a file, Choose file or drop file, No file chosen'
)
})
})
})
Expand Down Expand Up @@ -348,6 +344,61 @@ describe('/components/file-upload', () => {
})
})

describe('accessible name', () => {
beforeEach(async () => {})

it('includes the label, the status, the pseudo button and instruction', async () => {
await render(page, 'file-upload', examples.enhanced)

const $element = await page.$('.govuk-file-upload__button')

const accessibleName = await getAccessibleName(page, $element)
await expect(accessibleName.replaceAll(/\s+/g, ' ')).toBe(
'Upload a file , No file chosen , Choose file or drop file'
)
})

it('includes the label, file name, pseudo button and instruction once a file is selected', async () => {
await render(page, 'file-upload', examples.enhanced)

const $element = await page.$('.govuk-file-upload__button')

const [fileChooser] = await Promise.all([
page.waitForFileChooser(),
page.click(buttonSelector)
])
await fileChooser.accept(['fakefile.txt'])

const accessibleName = await getAccessibleName(page, $element)
await expect(accessibleName.replaceAll(/\s+/g, ' ')).toBe(
'Upload a file , fakefile.txt , Choose file or drop file'
)
})

it('includes the label, file name, pseudo button and instruction once a file is selected', async () => {
await render(page, 'file-upload', examples.enhanced, {
beforeInitialisation() {
document
.querySelector('[type="file"]')
.setAttribute('multiple', '')
}
})

const $element = await page.$('.govuk-file-upload__button')

const [fileChooser] = await Promise.all([
page.waitForFileChooser(),
page.click(buttonSelector)
])
await fileChooser.accept(['fakefile1.txt', 'fakefile2.txt'])

const accessibleName = await getAccessibleName(page, $element)
await expect(accessibleName.replaceAll(/\s+/g, ' ')).toBe(
'Upload a file , 2 files chosen , Choose file or drop file'
)
})
})

describe('i18n', () => {
beforeEach(async () => {
await render(page, 'file-upload', examples.translated)
Expand All @@ -363,15 +414,8 @@ describe('/components/file-upload', () => {
el.innerHTML.trim()
)

const buttonAriaText = await page.$eval(buttonSelector, (el) =>
el.getAttribute('aria-label')
)

expect(buttonElementText).toBe('Dewiswch ffeil')
expect(statusElementText).toBe("Dim ffeiliau wedi'u dewis")
expect(buttonAriaText).toBe(
"Llwythwch ffeil i fyny, Dewiswch ffeil neu ollwng ffeil, Dim ffeiliau wedi'u dewis"
)
})

describe('status element', () => {
Expand Down