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 options by passing current values #2616

Merged
merged 3 commits into from
Jun 20, 2022
Merged

Conversation

36degrees
Copy link
Contributor

@36degrees 36degrees commented May 13, 2022

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 / .

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.

Allow the user to pass the current value (or array of values, for checkboxes), and using that to work out which item(s) should be checked or selected.

The above example can then be done like this:

{{ 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’]
}) }}

Naming the new option has presented a few challenges – I wanted to avoid using checked or selected so that we don’t have options at different ‘levels’ with the same name but accepting different types of values – booleans for individual items and strings / arrays at the top level.

I also felt that value was not an accurate description for what the option did, and may also be confused with item.value (and the top-level value option on other form controls such as text inputs).

I’ve ended up combining the two whilst also trying to be consistent with the underlying HTML attribute, and also reflecting whether the option accepts a string or an array. This does unfortunately mean that the option name is different for each of the three components:

  • checkedValue for Radios, where a string is passed to check a single radio button
  • checkedValues for Checkboxes, where an array of strings is passed to check one or more checkboxes
  • selectedValue for the Select, where a string is passed to select a single option.

I think on balance, although I can see how this may be confusing for users, it is logical and consistent within each individual component.

After further discussion within the team off the back of #2616 (comment) we ended up going with value / values.

Closes #2449.

@36degrees 36degrees modified the milestone: [NEXT] May 13, 2022
CHANGELOG.md Outdated

This change was introduced in [pull request #2616: Allow selecting options by passing current values](https://github.com/alphagov/govuk-frontend/pull/2616).

#### Select an option in a Select by using the `selectedValue` option
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

THIS IS A NIGHTMARE. Two of these words have two different meanings and we use both meanings in the same heading.

Select (verb) an option (an <option> HTML element within a <select>) in a Select (component name / HTML element name) using the selectedValue option (a 'Nunjucks macro option')

Suggestions welcome.

Copy link
Contributor

@EoinShaughnessy EoinShaughnessy May 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tricky one alright! Ok, first pass:

Choose an option in a select component by using the selectedValue parameter

Who is it that does the 'choosing' or 'selecting' of the option? Is the idea here that the service pre-selects something to save the end user time?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be to save the user time, or more likely it might be because the user has gone back to change an answer they previously gave.

For example, if a user was in the middle of a transaction and went 'back' to a previous page that used a select, the answer they previously gave should be selected when the page loads.

Likewise, if the user was checking their answers and decided to change an answer they previously gave, when they click change the page that loads should pre-select the answer that they'd previously given.

CHANGELOG.md Outdated Show resolved Hide resolved
CHANGELOG.md Outdated Show resolved Hide resolved
@36degrees
Copy link
Contributor Author

@EoinShaughnessy the changelog entry for this PR is a bit gnarly – could do with your thoughts on it when you have a mo. There's no rush on it though, I don't think we should try and get this into v4.1.0 so it can wait for the next release.

@36degrees
Copy link
Contributor Author

I've raised a PR with a proof of concept of how this could be extended in the Prototype Kit to allow pre-selecting radios and checkboxes from the data object without the user having to do anything, which seems kinda neat: alphagov/govuk-prototype-kit#1316

CHANGELOG.md Outdated Show resolved Hide resolved
CHANGELOG.md Outdated Show resolved Hide resolved
CHANGELOG.md Outdated Show resolved Hide resolved
CHANGELOG.md Outdated Show resolved Hide resolved
CHANGELOG.md Outdated Show resolved Hide resolved
CHANGELOG.md Outdated Show resolved Hide resolved
CHANGELOG.md Outdated Show resolved Hide resolved
CHANGELOG.md Outdated Show resolved Hide resolved
CHANGELOG.md Outdated Show resolved Hide resolved
Copy link
Contributor

@EoinShaughnessy EoinShaughnessy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a few small comments. Content looks good though! Separating it out was a neat idea.

@lfdebrux
Copy link
Member

lfdebrux commented May 17, 2022

Naming the new option has presented a few challenges – I wanted to avoid using checked or selected so that we don’t have options at different ‘levels’ with the same name but accepting different types of values – booleans for individual items and strings / arrays at the top level.

I also felt that value was not an accurate description for what the option did, and may also be confused with item.value (and the top-level value option on other form controls such as text inputs).

I'd argue strongly for calling it value. I agree that it is not a completely accurate description, but because users will be used to value from other macros, using value here will be the least surprising, in my opinion. I think having the parameter named different for different macros will add friction, and I'm not sure whether there would be a benefit to it.

@36degrees
Copy link
Contributor Author

I'd argue strongly for calling it value. I agree that it is not a completely accurate description, but because users will be used to value from other macros, using value here will be the least surprising, in my opinion. I think having the parameter named different for different macros will add friction, and I'm not sure whether there would be a benefit to it.

Interesting! I understand your argument but I'd argue that using value is surprising if you understand the underlying HTML simply because that's not how the value attribute works for radios, checkboxes or options in a select.

We also have a principle that says we should mimic HTML attribute names and although it's not explicitly stated there I think it also makes sense to avoid using common HTML attribute names when the option doesn't translate directly to that attribute on any of the elements within the component.

(Having said that… we already use value in the macro for the textarea component, even though the <textarea> HTML element doesn't accept a value and instead uses the contents of the element… so maybe that ship has sailed)

Fundamentally… I'm wondering whether we're better off trying to 'abstract away' some of the messiness and inconsistencies in the HTML spec, or to try to expose it and be consistent with it? 🤔

Using value (and values?)

This option seems better for users who aren't familiar with the underlying HTML, for example Prototype Kit users.

However, if we go ahead with something like alphagov/govuk-prototype-kit#1316 to handle this automatically based on the data object, how often will users actually need to use this option?

  • ✅ Consistent with the existing API for text inputs and textareas
  • ✅ Guessable? This feels like something that some users might 'just try'
  • ❓ Consistent between radios, checkboxes and select – although we might want to use values for checkboxes, given checkboxes expects an array?
  • 🟥 Not consistent with the affected HTML attribute
  • 🟥 Potentially confusing given we're using item.value which means something different.

Using checkedValue / checkedValues / selectedValue

This option seems better for users who are thinking about the change they want to make to the underlying HTML?

  • 🟥 Not consistent with text inputs and textareas (but neither is HTML)
  • 🟥 Likely only to be discovered by users who read the API docs
  • 🟥 Different option name for the three different components
  • ✅ Consistent with the affected HTML attribute
  • ✅ Not 'in conflict' with item.value

@36degrees 36degrees force-pushed the checked-values branch 3 times, most recently from 7d78d62 to c23444f Compare May 20, 2022 16:14
Copy link
Contributor

@edwardhorsford edwardhorsford left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work! This is a bit similar to a filter we use to make the macros simpler.

Note you probably want the ability for a single item to have checked: false - at the moment the logic will overwrite these. Basically - if a single item has a value for checked or selected you should honour that and not override it.

This is helpful when most of the items just need to check if the stored value equals the value, but one or two need more complex logic to determine if they should be selected.

src/govuk/components/radios/template.njk Outdated Show resolved Hide resolved
src/govuk/components/select/template.njk Outdated Show resolved Hide resolved
36degrees added a commit that referenced this pull request May 23, 2022
If a checkbox, radio or select option has the checked or selected option set, always use its value to set the checked or selected state, even when it’s falsey.

As suggested by @edwardhorsford [1]:

> This is helpful when most of the items just need to check if the stored value equals the value, but one or two need more complex logic to determine if they should be selected.

[1]: #2616 (review)
CHANGELOG.md Outdated Show resolved Hide resolved
CHANGELOG.md Outdated Show resolved Hide resolved
CHANGELOG.md Outdated Show resolved Hide resolved
36degrees added a commit that referenced this pull request May 23, 2022
If a checkbox, radio or select option has the checked or selected option set, always use its value to set the checked or selected state, even when it’s falsey.

As suggested by @edwardhorsford [1]:

> This is helpful when most of the items just need to check if the stored value equals the value, but one or two need more complex logic to determine if they should be selected.

[1]: #2616 (review)
36degrees added a commit that referenced this pull request May 23, 2022
If a checkbox, radio or select option has the checked or selected option set, always use its value to set the checked or selected state, even when it’s falsey.

As suggested by @edwardhorsford [1]:

> This is helpful when most of the items just need to check if the stored value equals the value, but one or two need more complex logic to determine if they should be selected.

[1]: #2616 (review)
@36degrees 36degrees requested a review from a team May 23, 2022 13:27
@owenatgov owenatgov self-assigned this May 24, 2022
Copy link
Contributor

@owenatgov owenatgov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking really nice! I've left a couple of comments about tech docs. The solution itself is very slick 👍🏻

src/govuk/components/checkboxes/checkboxes.yaml Outdated Show resolved Hide resolved
@@ -69,7 +69,7 @@ params:
- name: checked
type: boolean
required: false
description: If `true`, radio will be checked.
description: If `true`, the radio will be checked when the page loads.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment on communicating the override

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
description: If `true`, the radio will be checked when the page loads.
description: Whether the radio should be checked when the page loads. If you set this option, the `value` option will not affect the checked state of the radio.

src/govuk/components/radios/radios.yaml Show resolved Hide resolved
@@ -23,7 +23,7 @@ params:
- name: selected
type: boolean
required: false
description: Sets the option as the selected.
description: Sets the option as selected when the page loads.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment on communicating the override

36degrees added a commit that referenced this pull request May 26, 2022
If a checkbox, radio or select option has the checked or selected option set, always use its value to set the checked or selected state, even when it’s falsey.

As suggested by @edwardhorsford [1]:

> This is helpful when most of the items just need to check if the stored value equals the value, but one or two need more complex logic to determine if they should be selected.

[1]: #2616 (review)
36degrees and others added 3 commits May 26, 2022 16:22
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.

Allow the user to pass the current value (or array of values, for checkboxes), and using that to work out which item(s) should be checked or selected.

The above example can then be done like this:

```
{{ 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’]
}) }}
```
Co-authored-by: EoinShaughnessy <72507742+EoinShaughnessy@users.noreply.github.com>
If a checkbox, radio or select option has the checked or selected option set, always use its value to set the checked or selected state, even when it’s falsey.

As suggested by @edwardhorsford [1]:

> This is helpful when most of the items just need to check if the stored value equals the value, but one or two need more complex logic to determine if they should be selected.

[1]: #2616 (review)
@36degrees
Copy link
Contributor Author

I think I've addressed the comments from @owenatgov but it could do with another once over. Then I believe this is ready to merge once v4.1.1 is out.

@36degrees 36degrees requested a review from owenatgov May 26, 2022 15:25
@govuk-design-system-ci govuk-design-system-ci temporarily deployed to govuk-frontend-pr-2616 May 30, 2022 10:12 Inactive
Copy link
Contributor

@owenatgov owenatgov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking great 👍🏻

@owenatgov owenatgov modified the milestone: v4.2.0 Jun 17, 2022
@36degrees
Copy link
Contributor Author

Then I believe this is ready to merge once v4.1.1 is out.

We've now decided to skip v4.1.1 and roll everything into v4.2.0, so this can be merged.

@36degrees 36degrees merged commit 8928ddf into main Jun 20, 2022
@36degrees 36degrees deleted the checked-values branch June 20, 2022 14:44
@domoscargin domoscargin mentioned this pull request Jun 27, 2022
querkmachine pushed a commit that referenced this pull request Oct 17, 2022
If a checkbox, radio or select option has the checked or selected option set, always use its value to set the checked or selected state, even when it’s falsey.

As suggested by @edwardhorsford [1]:

> This is helpful when most of the items just need to check if the stored value equals the value, but one or two need more complex logic to determine if they should be selected.

[1]: #2616 (review)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

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