Skip to content

Commit

Permalink
Use CSS to reserve space rather than HTML
Browse files Browse the repository at this point in the history
Use a zero-width space at the end of the content to ensure there's content in the element so that the layout does not shift when JavaScript kicks in
  • Loading branch information
romaricpascal committed Nov 2, 2022
1 parent befed90 commit 1054913
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 16 deletions.
9 changes: 9 additions & 0 deletions src/govuk/components/character-count/_index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@
@include govuk-font($size: false, $tabular: true);
margin-top: 0;
margin-bottom: 0;

&:after {
// Zero-width space that will reserve vertical space when no hint is provided
// as:
// - setting a min-height is not possible without a magic number
// because the line-height is set by the `govuk-font` call above
// - using `:empty` is not possible as the hint macro outputs line breaks
content: "\200B";
}
}

.govuk-character-count__message--disabled {
Expand Down
15 changes: 7 additions & 8 deletions src/govuk/components/character-count/character-count.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -430,11 +430,9 @@ describe('Character count', () => {
expect(visibility).toEqual('visible')
})

it('fills the accessible description of the textarea once the maximum is known', async () => {
// This tests that any fallback hint provided through data-attributes
// (or the Nunjucks macro) waiting for a maximum to be provided in JavaScript config
// will lead to the message being injected in the element holding the textarea's accessible description
// (and interpolated to replace `%{count}` with the maximum)
it('configures the description of the textarea', async () => {
// This tests that a description can be provided through JavaScript attributes
// and interpolated with the limit provided to the character count in JS.

await renderAndInitialise(page, 'character-count', {
nunjucksParams:
Expand Down Expand Up @@ -608,10 +606,11 @@ describe('Character count', () => {
expect(message).toEqual('You have 1 word too many')
})

it('fills interpolates the fallback hint in data attributes with the maximum set in JavaScript', async () => {
it('interpolates the fallback hint in data attributes with the maximum set in JavaScript', async () => {
// This tests that any fallback hint provided through data-attributes
// (or the Nunjucks macro) waiting for a maximum to be provided in JavaScript config
// will lead to the message being injected in the element holding the textarea's accessible description
// (or the Nunjucks macro), waiting for a maximum to be provided in
// JavaScript config, will lead to the message being injected in the
// element holding the textarea's accessible description
// (and interpolated to replace `%{count}` with the maximum)

await renderAndInitialise(page, 'character-count', {
Expand Down
7 changes: 3 additions & 4 deletions src/govuk/components/character-count/template.njk
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,12 @@
{%- set fallbackHintLength = params.maxwords or params.maxlength %}
{%- set fallbackHintText = params.fallbackHintText or 'You can enter up to %{count} ' + ('words' if params.maxwords else 'characters') %}
{#
Use the fact that `html` has precedence over `text` to override content
when no limit is set and reserve vertical space to avoid layout shifting
by injecting a non-breakable space
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
were provided to the macro.
#}
{{ govukHint({
text: ((fallbackHintText) | replace('%{count}', fallbackHintLength) if not hasNoLimit),
html: (' ' if hasNoLimit),
id: params.id + '-info',
classes: 'govuk-character-count__message' + (' ' + params.countMessage.classes if params.countMessage.classes)
}) }}
Expand Down
7 changes: 3 additions & 4 deletions src/govuk/components/character-count/template.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -269,11 +269,10 @@ describe('Character count', () => {
const $component = $('[data-module]')
expect($component.attr('data-i18n.fallback-hint.other')).toEqual('No more than %{count} characters')

// A non-breaking space is set as the count message to reserve space
// and prevent a layout shift when the JavaScript component
// initialises and fills the element with the relevant text
// No content is set as the accessible description cannot be interpolated on the backend
// It'll be up to the JavaScript to fill it in
const $countMessage = $('.govuk-character-count__message')
expect($countMessage.html()).toContain(' ')
expect($countMessage.html()).toMatch(/^\s*$/) // The macro outputs linebreaks around the hint itself
})
})

Expand Down

0 comments on commit 1054913

Please sign in to comment.