Functional Weave
Code in Rust

form.character-count@1.0.1

README.md

3,314 bytes · view raw

# form.character-count

The message under a text box with a limit, worded exactly as the GOV.UK
Design System's character count: "You have 12 characters remaining", "You
have 1 character remaining", "You have 0 characters remaining", "You have 3
characters too many", "You have 1 word too many". It returns the numbers as
well, so a form can mark the box in error when `over`, and the server can
refuse the same text with the same count:

```ts
characterCount("Hello", 10, "characters", null)
// { count: 5, remaining: 5, over: false, visible: true,
//   message: "You have 5 characters remaining",
//   description: "You can enter up to 10 characters" }
```

`react.form.textarea` shows it live as the person types. It is written in all
three languages because the server has to enforce the same limit: a count the
browser and the API disagree on is a form that says "0 remaining" and then
rejects the post.

## Decisions

- **Characters are Unicode code points.** An emoji is 1, not the 2 UTF-16
  units JavaScript's `length` (and GOV.UK's own script) counts, so all three
  languages agree. A decomposed accent (`e` plus a combining acute) is 2.
  Text is counted as given: a browser posts a line break in a textarea as
  `\r\n`, which is 2 characters, so normalise line breaks before counting on
  the server if the limit must match the browser's.
- **Words are runs of anything but whitespace**, like GOV.UK's `/\S+/g`, with
  whitespace spelled out as the Unicode White_Space set (tab to carriage
  return, space, U+0085, no-break space, U+1680, U+2000-U+200A, the line and
  paragraph separators, U+202F, U+205F, U+3000) so every language splits
  alike. Punctuation does not split: `two,three` is one word.
- **The threshold** is a percentage of the limit below which the message is
  not shown (`visible` false), as GOV.UK's `threshold` option. The comparison
  is `count * 100 >= limit * threshold` in whole numbers, so 50% of 3 is 1.5
  and one character stays below it. `null` shows the message always; so does
  being over the limit, whatever the threshold. The message is worked out even
  when not visible.
- **remaining** goes negative when over; `message` says the positive number
  "too many".
- **description** is GOV.UK's `textareaDescriptionText`, "You can enter up to
  200 characters" (singular for a limit of 1), for a page before its script
  runs.

Errors, identical in every language: `limit must be a whole number of at
least 1`, `threshold must be a whole number from 0 to 100`, `unit must be
characters or words`.

Sources: GOV.UK Design System, "Character count"
https://design-system.service.gov.uk/components/character-count/; the wording
and the threshold rule from govuk-frontend's `character-count.mjs`
(`charactersUnderLimit`, `charactersAtLimit`, `charactersOverLimit`,
`wordsUnderLimit`, `wordsAtLimit`, `wordsOverLimit`, `isOverThreshold`)
https://github.com/alphagov/govuk-frontend/blob/main/packages/govuk-frontend/src/govuk/components/character-count/character-count.mjs.

## Notices

Portions derived from GOV.UK Frontend (https://github.com/alphagov/govuk-frontend), Copyright (c) 2017 Crown Copyright (Government Digital Service), under the MIT License; the full notice is in NOTICE.

1.0.1 adds its attribution notices (NOTICE). The code and the tests are unchanged.