Skip to content

Commit

Permalink
Merge pull request #5658 from alphagov/form-control-default-ids
Browse files Browse the repository at this point in the history
Update form control components to set a default `id` based on `name`
  • Loading branch information
querkmachine authored Jan 31, 2025
2 parents 60c7952 + 155f264 commit b749005
Show file tree
Hide file tree
Showing 19 changed files with 159 additions and 53 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ For advice on how to use these release notes see [our guidance on staying up to

## Unreleased

### New features

#### Form control components now have default `id` attributes

If you're using the included Nunjucks macros, the Text input, Textarea, Password input, Character count, File upload, and Select components now automatically use the value of the `name` parameter for the `id` parameter.

This means that you only have to provide the `name` parameters if they both have the same value.

Note that the Date input component still requires an `id` attribute.

This change was introduced in [pull request #5658: Update form control components to set a default `id` based on `name`](https://github.com/alphagov/govuk-frontend/pull/5658).

### Deprecated features

#### Migrate to the new organisation colour palette
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
params:
- name: id
type: string
required: true
description: The ID of the textarea.
required: false
description: The ID of the textarea. Defaults to the value of `name`.
- name: name
type: string
required: true
Expand Down Expand Up @@ -135,7 +135,6 @@ examples:
- name: default
options:
name: more-detail
id: more-detail
maxlength: 10
label:
text: Can you provide more detail?
Expand Down Expand Up @@ -255,6 +254,14 @@ examples:
label:
text: With classes
classes: app-character-count--custom-modifier
- name: id
hidden: true
options:
id: character-count-id
name: test-name
maxlength: 10
label:
text: With custom id
- name: attributes
hidden: true
options:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@
{%- set textareaDescriptionLength = params.maxwords or params.maxlength -%}
{%- set textareaDescriptionText = params.textareaDescriptionText or 'You can enter up to %{count} ' + ('words' if params.maxwords else 'characters') -%}
{%- set textareaDescriptionTextNoLimit = textareaDescriptionText | replace('%{count}', textareaDescriptionLength) if not hasNoLimit -%}
{%- set id = params.id if params.id else params.name -%}

{%- set countMessageHtml %}
{{ govukHint({
text: textareaDescriptionTextNoLimit,
id: params.id + '-info',
id: id + '-info',
classes: 'govuk-character-count__message' + (' ' + params.countMessage.classes if params.countMessage.classes)
}) | trim }}
{% if params.formGroup.afterInput %}
Expand Down Expand Up @@ -91,9 +92,9 @@
{% endfor -%}

{{ govukTextarea({
id: params.id,
id: id,
name: params.name,
describedBy: params.id + '-info',
describedBy: id + '-info',
rows: params.rows,
spellcheck: params.spellcheck,
value: params.value,
Expand All @@ -112,7 +113,7 @@
classes: params.label.classes,
isPageHeading: params.label.isPageHeading,
attributes: params.label.attributes,
for: params.id
for: id
},
hint: params.hint,
errorMessage: params.errorMessage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ describe('Character count', () => {
})

describe('default example', () => {
it('renders with id', () => {
it('autopopulates default id from name', () => {
const $ = render('character-count', examples.default)

const $component = $('.govuk-js-character-count')
expect($component.attr('id')).toBe('more-detail')
expect($component.attr('id')).toBe($component.attr('name'))
})

it('renders with name', () => {
Expand All @@ -35,6 +35,14 @@ describe('Character count', () => {
})

describe('custom options', () => {
it('renders with id', () => {
const $ = render('character-count', examples.id)

const $component = $('.govuk-js-character-count')
expect($component.attr('id')).not.toBe($component.attr('name'))
expect($component.attr('id')).toBe('character-count-id')
})

it('renders with classes', () => {
const $ = render('character-count', examples.classes)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ params:
description: The name of the input, which is submitted with the form data.
- name: id
type: string
required: true
description: The ID of the input.
required: false
description: The ID of the input. Defaults to the value of `name`.
- name: value
type: string
required: false
Expand Down Expand Up @@ -85,7 +85,6 @@ params:
examples:
- name: default
options:
id: file-upload-1
name: file-upload-1
label:
text: Upload a file
Expand Down Expand Up @@ -149,6 +148,13 @@ examples:
label:
text: Upload a file
classes: app-file-upload--custom-modifier
- name: id
hidden: true
options:
id: file-upload-id
name: test-name
label:
text: Upload a file
- name: with describedBy
hidden: true
options:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

{#- a record of other elements that we need to associate with the input using
aria-describedby – for example hints or error messages -#}
{% set describedBy = params.describedBy if params.describedBy else "" %}
{%- set describedBy = params.describedBy if params.describedBy else "" -%}
{%- set id = params.id if params.id else params.name -%}

<div class="govuk-form-group {%- if params.errorMessage %} govuk-form-group--error{% endif %} {%- if params.formGroup.classes %} {{ params.formGroup.classes }}{% endif %}"
{{- govukAttributes(params.formGroup.attributes) }}>
{{ govukLabel({
Expand All @@ -14,10 +16,10 @@
classes: params.label.classes,
isPageHeading: params.label.isPageHeading,
attributes: params.label.attributes,
for: params.id
for: id
}) | trim | indent(2) }}
{% if params.hint %}
{% set hintId = params.id + '-hint' %}
{% set hintId = id + '-hint' %}
{% set describedBy = describedBy + ' ' + hintId if describedBy else hintId %}
{{ govukHint({
id: hintId,
Expand All @@ -28,7 +30,7 @@
}) | trim | indent(2) }}
{% endif %}
{% if params.errorMessage %}
{% set errorId = params.id + '-error' %}
{% set errorId = id + '-error' %}
{% set describedBy = describedBy + ' ' + errorId if describedBy else errorId %}
{{ govukErrorMessage({
id: errorId,
Expand All @@ -42,7 +44,7 @@
{% if params.formGroup.beforeInput %}
{{ params.formGroup.beforeInput.html | safe | trim | indent(2) if params.formGroup.beforeInput.html else params.formGroup.beforeInput.text }}
{% endif %}
<input class="govuk-file-upload {%- if params.classes %} {{ params.classes }}{% endif %} {%- if params.errorMessage %} govuk-file-upload--error{% endif %}" id="{{ params.id }}" name="{{ params.name }}" type="file"
<input class="govuk-file-upload {%- if params.classes %} {{ params.classes }}{% endif %} {%- if params.errorMessage %} govuk-file-upload--error{% endif %}" id="{{ id }}" name="{{ params.name }}" type="file"
{%- if params.value %} value="{{ params.value }}"{% endif %}
{%- if params.disabled %} disabled{% endif %}
{%- if describedBy %} aria-describedby="{{ describedBy }}"{% endif %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ describe('File upload', () => {
})

describe('default example', () => {
it('renders with id', () => {
it('autopopulates default id from name', () => {
const $ = render('file-upload', examples.default)

const $component = $('.govuk-file-upload')
expect($component.attr('id')).toBe('file-upload-1')
expect($component.attr('id')).toBe($component.attr('name'))
})

it('renders with name', () => {
Expand All @@ -36,6 +36,14 @@ describe('File upload', () => {
})

describe('custom options', () => {
it('renders with id', () => {
const $ = render('file-upload', examples.id)

const $component = $('.govuk-file-upload')
expect($component.attr('id')).not.toBe($component.attr('name'))
expect($component.attr('id')).toBe('file-upload-id')
})

it('renders with classes', () => {
const $ = render('file-upload', examples.classes)

Expand Down
12 changes: 9 additions & 3 deletions packages/govuk-frontend/src/govuk/components/input/input.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
params:
- name: id
type: string
required: true
description: The ID of the input.
required: false
description: The ID of the input. Defaults to the value of `name`.
- name: name
type: string
required: true
Expand Down Expand Up @@ -166,7 +166,6 @@ examples:
options:
label:
text: National Insurance number
id: input-example
name: test-name
- name: with hint text
options:
Expand Down Expand Up @@ -397,6 +396,13 @@ examples:
label:
text: With classes
classes: app-input--custom-modifier
- name: id
hidden: true
options:
id: input-id
name: testing-name
label:
text: With custom id
- name: custom type
hidden: true
options:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ describe('Input', () => {
$label = document.querySelector('.govuk-label')
})

it('sets the `id` attribute based on the `id` option', () => {
expect($component).toHaveAttribute('id', 'input-example')
it('autopopulates `id` based on the `name` option', () => {
expect($component).toHaveAttribute('id', $component.name)
})

it('sets the `name` attribute based on the `name` option', () => {
Expand Down Expand Up @@ -65,6 +65,13 @@ describe('Input', () => {
})

describe('custom options', () => {
it('sets the `id` attribute based on the `id` option', () => {
document.body.innerHTML = render('input', examples.id)

const $component = document.querySelector('.govuk-input')
expect($component).toHaveAttribute('id', 'input-id')
})

it('includes additional classes from the `classes` option', () => {
document.body.innerHTML = render('input', examples.classes)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
{#- a record of other elements that we need to associate with the input using
aria-describedby – for example hints or error messages -#}
{% set describedBy = params.describedBy if params.describedBy else undefined -%}
{%- set id = params.id if params.id else params.name -%}

{%- set hasPrefix = true if params.prefix and (params.prefix.text or params.prefix.html) else false %}
{%- set hasSuffix = true if params.suffix and (params.suffix.text or params.suffix.html) else false %}
Expand All @@ -27,7 +28,7 @@
<input
{{- govukAttributes({
class: classNames,
id: params.id,
id: id,
name: params.name,
type: params.type | default("text", true),
spellcheck: {
Expand Down Expand Up @@ -83,10 +84,10 @@
classes: params.label.classes,
isPageHeading: params.label.isPageHeading,
attributes: params.label.attributes,
for: params.id
for: id
}) | trim | indent(2) }}
{% if params.hint %}
{% set hintId = params.id + '-hint' %}
{% set hintId = id + '-hint' %}
{% set describedBy = describedBy + ' ' + hintId if describedBy else hintId %}
{{ govukHint({
id: hintId,
Expand All @@ -97,7 +98,7 @@
}) | trim | indent(2) }}
{% endif %}
{% if params.errorMessage %}
{% set errorId = params.id + '-error' %}
{% set errorId = id + '-error' %}
{% set describedBy = describedBy + ' ' + errorId if describedBy else errorId %}
{{ govukErrorMessage({
id: errorId,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
params:
- name: id
type: string
required: true
description: The ID of the input.
required: false
description: The ID of the input. Defaults to the value of `name`.
- name: name
type: string
required: true
Expand Down Expand Up @@ -124,7 +124,6 @@ examples:
options:
label:
text: Password
id: password-input
name: password
- name: with hint text
options:
Expand Down Expand Up @@ -189,6 +188,13 @@ examples:
label:
text: With classes
classes: app-input--custom-modifier
- name: id
hidden: true
options:
id: password-id
name: testing-name
label:
text: With custom id
- name: value
hidden: true
options:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
{% from "../button/macro.njk" import govukButton -%}
{% from "../input/macro.njk" import govukInput -%}

{%- set id = params.id if params.id else params.name -%}

{% set attributesHtml -%}
{{- ' data-module="govuk-password-input"' | safe }}

Expand Down Expand Up @@ -49,7 +51,7 @@
classes: "govuk-button--secondary govuk-password-input__toggle govuk-js-password-input-toggle" + (" " + params.button.classes if params.button.classes),
text: params.showPasswordText | default("Show"),
attributes: {
"aria-controls": params.id,
"aria-controls": id,
"aria-label": params.showPasswordAriaLabelText | default("Show password"),
"hidden": {
value: true,
Expand Down Expand Up @@ -78,7 +80,7 @@
hint: params.hint,
classes: "govuk-password-input__input govuk-js-password-input-input" + (" " + params.classes if params.classes),
errorMessage: params.errorMessage,
id: params.id,
id: id,
name: params.name,
type: "password",
spellcheck: false,
Expand Down
Loading

0 comments on commit b749005

Please sign in to comment.