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

[WIP] Define default id parameter values for various components #5171

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
params:
- name: id
type: string
required: true
required: false
description: Must be unique across the domain of your service if `rememberExpanded` is `true` (as the expanded state of individual instances of the component persists across page loads using [session storage](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage)). Used as an `id` in the HTML for the accordion as a whole, and also as a prefix for the `id`s of the section contents and the buttons that open them, so that those `id`s can be the target of `aria-control` attributes.
- name: headingLevel
type: integer
Expand Down Expand Up @@ -95,7 +95,6 @@ params:
examples:
- name: default
options:
id: default-example
items:
- heading:
text: Section A
Expand Down Expand Up @@ -235,6 +234,15 @@ examples:
</ul>

# Hidden examples are not shown in the review app, but are used for tests and HTML fixtures
- name: id
hidden: true
options:
id: custom-id
items:
- heading:
text: Section A
content:
text: Some content
- name: classes
hidden: true
options:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,18 @@ describe('Accordion', () => {
it('renders with id', () => {
const $component = document.querySelector('.govuk-accordion')

expect($component).toHaveAttribute('id', 'default-example')
expect($component).toHaveAttribute('id', 'accordion')
})
})

describe('custom options', () => {
it('renders with id', () => {
document.body.innerHTML = render('accordion', examples.id)
const $component = document.querySelector('.govuk-accordion')

expect($component).toHaveAttribute('id', 'custom-id')
})

it('renders with classes', () => {
document.body.innerHTML = render('accordion', examples.classes)
const $component = document.querySelector('.govuk-accordion')
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
{% from "../../macros/attributes.njk" import govukAttributes %}
{% from "../../macros/i18n.njk" import govukI18nAttributes %}

{%- set componentId = params.id | default("accordion") -%}

{%- macro _accordionItem(params, item, index) %}
{%- set headingLevel = params.headingLevel if params.headingLevel else 2 %}
<div class="govuk-accordion__section {%- if item.expanded %} govuk-accordion__section--expanded{% endif %}">
<div class="govuk-accordion__section-header">
<h{{ headingLevel }} class="govuk-accordion__section-heading">
<span class="govuk-accordion__section-button" id="{{ params.id }}-heading-{{ index }}">
<span class="govuk-accordion__section-button" id="{{ componentId }}-heading-{{ index }}">
{{ item.heading.html | safe | trim | indent(8) if item.heading.html else item.heading.text }}
</span>
</h{{ headingLevel }}>
{% if item.summary.html or item.summary.text %}
<div class="govuk-accordion__section-summary govuk-body" id="{{ params.id }}-summary-{{ index }}">
<div class="govuk-accordion__section-summary govuk-body" id="{{ componentId }}-summary-{{ index }}">
{{ item.summary.html | safe | trim | indent(8) if item.summary.html else item.summary.text }}
</div>
{% endif %}
</div>
<div id="{{ params.id }}-content-{{ index }}" class="govuk-accordion__section-content">
<div id="{{ componentId }}-content-{{ index }}" class="govuk-accordion__section-content">
{% if item.content.html %}
{{ item.content.html | safe | trim | indent(6) }}
{% elif item.content.text %}
Expand All @@ -28,7 +30,7 @@
</div>
{% endmacro -%}

<div class="govuk-accordion {%- if params.classes %} {{ params.classes }}{% endif %}" data-module="govuk-accordion" id="{{ params.id }}"
<div class="govuk-accordion {%- if params.classes %} {{ params.classes }}{% endif %}" data-module="govuk-accordion" id="{{ componentId }}"
{{- govukI18nAttributes({
key: 'hide-all-sections',
message: params.hideAllSectionsText
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 and also as a prefix for the `id`s of the textarea's hints, so that those `id`s can be the target of `aria-describedby` attributes. Defaults to matching `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
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
{% from "../textarea/macro.njk" import govukTextarea %}
{% from "../hint/macro.njk" import govukHint %}

{%- set componentId = params.id | default(params.name) -%}

{#-
If the limit is set in JavaScript, we won't be able to interpolate the message
until JavaScript, so we only set a text if the `maxlength` or `maxwords` options
Expand All @@ -16,7 +18,7 @@
{%- set countMessageHtml %}
{{ govukHint({
text: textareaDescriptionTextNoLimit,
id: params.id + '-info',
id: componentId + '-info',
classes: 'govuk-character-count__message' + (' ' + params.countMessage.classes if params.countMessage.classes)
}) | trim }}
{% if params.formGroup.afterInput %}
Expand Down Expand Up @@ -91,9 +93,9 @@
{% endfor -%}

{{ govukTextarea({
id: params.id,
id: componentId,
name: params.name,
describedBy: params.id + '-info',
describedBy: componentId + '-info',
rows: params.rows,
spellcheck: params.spellcheck,
value: params.value,
Expand All @@ -112,7 +114,7 @@
classes: params.label.classes,
isPageHeading: params.label.isPageHeading,
attributes: params.label.attributes,
for: params.id
for: componentId
},
hint: params.hint,
errorMessage: params.errorMessage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,17 @@ exports[`Date input nested dependant components passes through fieldset params w

exports[`Date input nested dependant components passes through label params without breaking 1`] = `
<label class="govuk-label govuk-date-input__label"
for="dob-day"
for="date-input-day"
>
Day
</label>
<label class="govuk-label govuk-date-input__label"
for="dob-month"
for="date-input-month"
>
Month
</label>
<label class="govuk-label govuk-date-input__label"
for="dob-year"
for="date-input-year"
>
Year
</label>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
params:
- name: id
type: string
required: true
description: This is used for the main component and to compose the ID attribute for each item.
required: false
description: This is used for the main component and to compose the ID attribute for each item. Defaults to `namePrefix` if set, otherwise uses `"date-input"`.
- name: namePrefix
type: string
required: false
Expand Down Expand Up @@ -109,8 +109,6 @@ params:

examples:
- name: default
options:
id: dob
- name: complete question
options:
id: dob
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
{% from "../hint/macro.njk" import govukHint %}
{% from "../input/macro.njk" import govukInput %}

{%- set componentId = (params.id if params.id else params.namePrefix) | default("date-input") %}

{#- 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.fieldset.describedBy if params.fieldset.describedBy else "" %}
Expand Down Expand Up @@ -33,7 +35,7 @@
{#- Capture the HTML so we can optionally nest it in a fieldset -#}
{% set innerHtml %}
{% if params.hint %}
{% set hintId = params.id + "-hint" %}
{% set hintId = componentId + "-hint" %}
{% set describedBy = describedBy + " " + hintId if describedBy else hintId %}
{{ govukHint({
id: hintId,
Expand All @@ -44,7 +46,7 @@
}) | trim | indent(2) }}
{% endif %}
{% if params.errorMessage %}
{% set errorId = params.id + "-error" %}
{% set errorId = componentId + "-error" %}
{% set describedBy = describedBy + " " + errorId if describedBy else errorId %}
{{ govukErrorMessage({
id: errorId,
Expand All @@ -57,7 +59,7 @@
{% endif %}
<div class="govuk-date-input {%- if params.classes %} {{ params.classes }}{% endif %}"
{{- govukAttributes(params.attributes) -}}
{%- if params.id %} id="{{ params.id }}"{% endif %}>
{%- if componentId %} id="{{ componentId }}"{% endif %}>
{% if params.formGroup.beforeInputs %}
{{ params.formGroup.beforeInputs.html | safe | trim | indent(4) if params.formGroup.beforeInputs.html else params.formGroup.beforeInputs.text }}
{% endif %}
Expand All @@ -68,7 +70,7 @@
text: item.label if item.label else item.name | capitalize,
classes: "govuk-date-input__label"
},
id: item.id if item.id else (params.id + "-" + item.name),
id: item.id if item.id else (componentId + "-" + item.name),
classes: "govuk-date-input__input " + (item.classes if item.classes),
name: (params.namePrefix + "-" + item.name) if params.namePrefix else item.name,
value: item.value,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe('Date input', () => {
const $ = render('date-input', examples.default)

const $component = $('.govuk-date-input')
expect($component.attr('id')).toBe('dob')
expect($component.attr('id')).toBe('date-input')
})

it('renders default inputs', () => {
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 matching `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
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
{% from "../hint/macro.njk" import govukHint %}
{% from "../label/macro.njk" import govukLabel %}

{%- set componentId = params.id | default(params.name) -%}

{#- 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 "" %}

<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 +17,10 @@
classes: params.label.classes,
isPageHeading: params.label.isPageHeading,
attributes: params.label.attributes,
for: params.id
for: componentId
}) | trim | indent(2) }}
{% if params.hint %}
{% set hintId = params.id + '-hint' %}
{% set hintId = componentId + '-hint' %}
{% set describedBy = describedBy + ' ' + hintId if describedBy else hintId %}
{{ govukHint({
id: hintId,
Expand All @@ -28,7 +31,7 @@
}) | trim | indent(2) }}
{% endif %}
{% if params.errorMessage %}
{% set errorId = params.id + '-error' %}
{% set errorId = componentId + '-error' %}
{% set describedBy = describedBy + ' ' + errorId if describedBy else errorId %}
{{ govukErrorMessage({
id: errorId,
Expand All @@ -42,7 +45,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="{{ componentId }}" 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
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 matching `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 @@ -389,6 +388,13 @@ examples:
value: QQ 12 34 56 C

# Hidden examples are not shown in the review app, but are used for tests and HTML fixtures
- name: id
hidden: true
options:
id: test-id
name: different-name
label:
text: With custom ID
- name: classes
hidden: true
options:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@ describe('Input', () => {
$label = document.querySelector('.govuk-label')
})

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

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

it('sets the `id` attribute based on the `name` option', () => {
const inputName = $component.getAttribute('name')
expect($component).toHaveAttribute('id', inputName)
})

it('sets the `type` attribute to a default value of "text"', () => {
expect($component).toHaveAttribute('type', 'text')
})
Expand Down Expand Up @@ -65,6 +66,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', 'test-id')
})

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

Expand Down
Loading
Loading