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

Allow selecting / checking values in checkboxes, radios and select by passing current value(s) #2449

Closed
36degrees opened this issue Nov 29, 2021 · 4 comments · Fixed by #2616

Comments

@36degrees
Copy link
Contributor

Related component

Checkboxes, radios and select

Context

We should consider allowing the user to pass the current value (or array of values, for checkboxes?), and using that to work out which item should be checked / selected.

For example, by introducing a new top level value param:

{{ govukSelect({
    id: "sort",
    name: "sort",
    label: {
    text: "Sort by"
    },
    items: [
        {
            value: "published",
            text: "Recently published"
        },
        {
            value: "updated",
            text: "Recently updated"
        },
        {
            value: "views",
            text: "Most views"
        },
        {
            value: "comments",
            text: "Most comments"
        }
    ],
    value: data['sort']
}) }}

Alternatives

At the minute the only way to pre-select or check specific items when using the Nunjucks macros for these components is to pass a boolean flag for each item, which then maps directly to the checked / selected HTML attribute for the <input> / <option>.

This means the user has to do the work to map the previous form data back to a boolean, resulting in code that might look something like this:

{{ govukSelect({
    id: "sort",
    name: "sort",
    label: {
        text: "Sort by"
    },
    items: [
        {
            value: "published",
            text: "Recently published",
            selected: (data['sort'] == "published")
        },
        {
            value: "updated",
            text: "Recently updated",
            selected: (data['sort'] == "updated")
        },
        {
            value: "views",
            text: "Most views",
            selected: (data['sort'] == "views")
        },
        {
            value: "comments",
            text: "Most comments",
            selected: (data['sort'] == "comments")
        }
    ]
}) }}

This is prone to introducing errors (especially when prototyping) – for example, any changes to the name of the field or the value of the item also need corresponding changes made to the boolean logic, potentially in multiple places.

Additional information (if applicable)

We should consider providing this in addition to the existing boolean attribute – if so, we'll need to be clear how the two interact with each other.

We should consider how this interacts with the checked helper in the Prototype Kit.

@joelanman
Copy link
Contributor

joelanman commented Nov 29, 2021

this is nice!

  • what if the attribute in the root had the html name for consistency (selected for select, checked for radios/checkboxes)? (I think 'value' is potentially ambiguous with the other use of 'value' in these elements)
  • would the attribute still be valid on each item as it is now? If so, they could conflict

@edwardhorsford
Copy link
Contributor

This would be a nice addition - what about default or defaultValue?

In the Becoming a teacher service line, we have a filter that essentially does this - originally written by @fofr, and modified by me. You can see an example of it in use here. It allows us to write much more terse UI macros, focusing on what's unique about them.

Among other things, if selected or checked is not provided on an item, then it compares the data provided with each item and marks as checked as needed.

would the attribute still be valid on each item as it is now? If so, they could conflict

If selected or checked is provided on an item it does nothing. This is nice where I want 9 out of 10 options to just compare against data, but one option needs more complex logic to determine if it should be checked.

@fofr
Copy link
Contributor

fofr commented Dec 2, 2021

Here are some docs on how that filter works:
https://govuk-prototype-rig.herokuapp.com/docs/using-data/form-components

Extract from that page

For example, to collect a user’s email address, you could write the following:

{{ govukInput({
  label: {
    text: "Email address"
  },
  id: "email-address",
  name: "account['email-address']"
  value: "data.account['email-address']"
}) }}

3 separate parameters are needed to store and display data. Each uses the same value, but formatted in a slightly different way:

  • id, which uses kebab case
  • name, which uses object notation
  • value, which also uses object notation, but proceeded by data.

Radios, checkboxes and select components also need to add a checked or selected attribute to any option that matches the stored value, and date inputs need individual values for day, month and year fields.

Decorating forms

The decorate function removes this overhead. This adds name, value, id (or idPrefix) and checked/selected attributes to a GOV.UK form component, with values based on where the data is stored.

The above example can now be rewritten like this:

{{ govukInput(decorate({
  label: {
    text: "Email address"
  }
}, "account['email-address']")) }}

@edwardhorsford
Copy link
Contributor

Note - @fofr's version and mine are subtly different in that I pass the data it should compare, and @fofr passes a string reference to the location of the data.

Using a string reference is how the checked() function works, but I find this really limiting as it forces you to only compare to exiting data in session data, rather than something you might have computed in the view. If the reference is deeply nested or you need to compute the location in the view, it can get really repetitive - you can't assign that data to a variable and pass the variable in.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Development

Successfully merging a pull request may close this issue.

5 participants