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

Input Label Accessibility Updates #514

Closed
wants to merge 22 commits into from
Closed

Conversation

danparnella
Copy link
Contributor

@danparnella danparnella commented Nov 19, 2021

  • Resolves aria-labelled by for LabeledField #436
  • Plan is also to add aria props to any input based component
  • Would also like to make sure the fieldset/legend setup is correct for checkbox and radio groups

Author Checklist

  • Add unit test(s)
  • Update version in package.json (see the versioning guidelines)
  • Update documentation (if necessary)
  • Add story to storybook (if necessary)
  • Assign dev reviewer

@danparnella danparnella linked an issue Nov 19, 2021 that may be closed by this pull request
@chawes13
Copy link
Contributor

@danparnella Can you do an analysis on how this will impact styling (both in storybook and in the client template)?

# Conflicts:
#	.storybook/styles/application.scss
#	.storybook/styles/base/_typography.scss
#	.storybook/styles/components/_activity.scss
#	.storybook/styles/components/_buttons.scss
#	.storybook/styles/components/_due-dates.scss
#	.storybook/styles/components/_expander.scss
#	.storybook/styles/components/_forms.scss
#	.storybook/styles/components/_grid.scss
#	.storybook/styles/components/_invites.scss
#	.storybook/styles/components/_navigation.scss
#	.storybook/styles/components/_profile-card.scss
#	.storybook/styles/components/_profile-section.scss
#	.storybook/styles/components/_purchased-profile-blade.scss
#	.storybook/styles/components/_recruiter-search.scss
#	.storybook/styles/components/_reminders.scss
#	.storybook/styles/components/_search-results-and-filtering.scss
#	.storybook/styles/components/_sidebar-content.scss
#	.storybook/styles/components/_sign-up-blocks.scss
#	.storybook/styles/components/_spinner.scss
#	.storybook/styles/components/_summit-list.scss
#	.storybook/styles/components/_tabs.scss
#	.storybook/styles/pages/_main.scss
#	.storybook/styles/pages/_styleguide.scss
#	.storybook/styles/settings/mixins/_layout.scss
#	.storybook/styles/settings/variables/_colors.scss
#	package.json
#	src/controls/color-picker.js
#	src/controls/nav-link.js
#	src/controls/tab-bar/tab-bar.js
#	src/forms/buttons/button.js
#	src/forms/helpers/blur-dirty.js
#	src/forms/helpers/button-classes.js
#	src/forms/helpers/dropdown-select.js
#	src/forms/helpers/field-prop-types.js
#	src/forms/helpers/replace-empty-string-value.js
#	src/forms/inputs/checkbox-group.js
#	src/forms/inputs/checkbox.js
#	src/forms/inputs/cloudinary-file-input.js
#	src/forms/inputs/color-input.js
#	src/forms/inputs/date-input.js
#	src/forms/inputs/dropdown-checkbox-group.js
#	src/forms/inputs/file-input/file-input.js
#	src/forms/inputs/file-input/file-preview.js
#	src/forms/inputs/input.js
#	src/forms/inputs/masked-input.js
#	src/forms/inputs/radio-group.js
#	src/forms/inputs/range-input.js
#	src/forms/inputs/setter-link.js
#	src/forms/inputs/switch.js
#	src/forms/labels/input-error.js
#	src/forms/labels/input-label.js
#	src/forms/labels/labeled-field.js
#	src/indicators/spinner.js
#	src/routes/authorized-route.js
#	src/routes/index.js
#	src/routes/unauthorized-route.js
#	src/tables/components/table-row.js
#	src/tables/sortable-table.js
#	stories/controls/nav-link.story.js
#	stories/controls/tab-bar.story.js
#	stories/dynamic-input.js
#	stories/forms/inputs/checkbox-group.story.js
#	stories/forms/inputs/file-input.story.js
#	stories/forms/inputs/input.story.js
#	stories/forms/inputs/radio-group.story.js
#	stories/forms/inputs/range-input.story.js
#	stories/forms/inputs/setter-link.story.js
#	stories/forms/labels/labeled-field.story.js
#	stories/tables/sortable-table.story.js
#	stories/tables/table.story.js
@danparnella danparnella changed the base branch from add-prettier to main January 10, 2023 00:03
@@ -48,10 +48,8 @@ $output-bourbon-deprecation-warnings: false;
@import 'components/progress-circle';
@import 'components/login';
@import 'components/pagination';
@import 'components/sidebar-content';
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I noticed a couple style files were no longer being used. Not sure if they should still be there for reference?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm fine with deleting them!

Comment on lines +4 to +5
fieldset,
.field-wrapper {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

My hope is that this might ease some of the style changes (just swapping fieldset for .field-wrapper in stylesheets). Hopefully most projects can just find and replace in their stylesheets without too much auditing.

Copy link
Contributor

Choose a reason for hiding this comment

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

Can you add this documentation to a migration guide?

Comment on lines +90 to +101
.input-wrapper {
position: relative;

i {
@include position(absolute, 3px null null 10px);
background-repeat: no-repeat;
background-size: 15px;
display: inline-block;
height: 20px;
width: 20px;
}
}
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 was included further down in a duplicate "Input Icon" section.

@@ -1,6 +1,6 @@
{
"name": "@launchpadlab/lp-components",
"version": "8.0.0",
"version": "9.0.0",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I figured I'd hold off on writing a migration guide until the code changes are approved.

src/forms/inputs/checkbox-group.js Show resolved Hide resolved
Comment on lines +64 to +69
<div>
<FieldsetLegend {...{ label, name }} id={inputId} />
<DropdownSelect selectedValues={value} className="checkboxes">
<CheckboxGroup {...props} label={false} />
<CheckboxGroup {...props} label={false} ariaLabelledby={inputId} />
</DropdownSelect>
</fieldset>
</div>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The checkbox group is actually the fieldset so div should be correct here and since a label/legend isn't provided, I'm using the id on the "outer" legend to provide the aria labelledby.

Copy link
Contributor

Choose a reason for hiding this comment

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

If we aren't using a fieldset, is legend the semantically appropriate tag? Or would p/span suffice?

Looking at the specs, the only Permitted parent is a fieldset https://developer.mozilla.org/en-US/docs/Web/HTML/Element/legend#technical_summary

* This component is used within {@link LabeledFieldset}, and therefore is incorporated into most fieldset grouped input components ({@link RadioGroup}, {@link CheckboxGroup}) by default.
*
* The text of the legend is set using the following rules:
* - If the `label` prop is set to `false`, the legend is hidden completely
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I kept debating whether to change "label" fully to "legend", but I was thinking for backwards compatibility (potentially in code, but also in terms of consistency mentally), sticking with label as the prop might be better. It's also still a "label", just a different kind of one. BUT, that's just my two cents.

@danparnella danparnella marked this pull request as ready for review March 10, 2023 23:55
@danparnella
Copy link
Contributor Author

@jhp0621 Tagging you for visibility!

{...props}
>
{optionObjects.map((option, i) => {
<LabeledFieldset className={className} {...props}>
Copy link
Contributor

Choose a reason for hiding this comment

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

@danparnella what happens if label is false? Pretty certain <fieldset> must have a unique <legend>; if a radio group has label set to false, I believe we would still be rendering a <fieldset> but without a <legend>?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, that's correct. If the design requires label={false}, then the developer should use aria-labelledby to specify which element to use as the "legend". It might be good for me to add that as an example in the story though.

Copy link
Contributor

Choose a reason for hiding this comment

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

It sounds like Ji ran into some issues where aria-labelledby on a fieldset still fails accessibility checks because a legend is required when using a fieldset (reference). An alternative might be to have <div role="group" aria-labelledby=""> in this instance.

The reference above is a technique which isn't required to meet WCAG, but is rather a guideline. We might need to dig more into the actual Success Criterion to see if this is a strict requirement or rather a strict interpretation on the accessibility tool. Maybe we could do testing with SiteImprove and Axe's accessibility testing extensions?

@jhp0621 Am I remembering correctly? Do you have more information/resources that you can share?

Copy link
Contributor

Choose a reason for hiding this comment

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

I just did some testing here, and the output is the exact same on VoiceOver. @jhp0621 maybe something to dig into a little deeper with the EA report. Does it list which success criterion are failing?

@@ -48,10 +48,8 @@ $output-bourbon-deprecation-warnings: false;
@import 'components/progress-circle';
@import 'components/login';
@import 'components/pagination';
@import 'components/sidebar-content';
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm fine with deleting them!

Comment on lines +4 to +5
fieldset,
.field-wrapper {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you add this documentation to a migration guide?

@@ -145,8 +159,7 @@ Radio
Checkbox
-----------------------*/
.checkbox {
@include rem(margin-top, 15px);
width: auto;
@include rem(margin-top, 3px);
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you explain this change?

@@ -567,7 +557,7 @@ Custom Dropdown Select
}
}

fieldset.checkbox,
.field-wrapper.checkbox,
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this is a one-for-one change. If we have rules for fieldset.checkbox and fieldset.CheckboxGroup, then I believe we would additionally need field-wrapper.checkbox and field-wrapper.CheckboxGroup, eh?

return (
<Checkbox // eslint-disable-line react/jsx-key
<Checkbox
key={option.value}
Copy link
Contributor

Choose a reason for hiding this comment

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

😎

.add('with custom label', () => {
// eslint-disable-next-line
const CustomLabel = ({ onClickLabel }) => (
<legend onClick={onClickLabel} htmlFor="inputName">
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we pass a different custom prop (or use it in a different way)? I don't want to encourage inappropriate onClick handler usage

})
.add('with custom error', () => {
const CustomError = (props) => (
<span id="inputError">
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we change this to inputNameError or use the generate input error id util here? I don't want to encourage the wrong usage of a custom error component (the aria-describedby that's added to the input won't match this id)

const onClick = jest.fn()
const wrapper = mount(
<FieldsetLegend name={name}>
Are you <span onClick={onClick}>sure</span>?
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we use the semantically appropriate <button> here instead?

Copy link
Contributor

Choose a reason for hiding this comment

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

Or remove it? Doesn't seem relevant to the test 🤔

const wrapper = mount(
<FieldsetLegend name={name}>
Are you{' '}
<span id="click" onClick={onClick}>
Copy link
Contributor

Choose a reason for hiding this comment

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

Please use a semantically appropriate element (i.e., button)

</LabeledFieldset>
)
expect(wrapper.find('fieldset').exists()).toEqual(true)
expect(wrapper.find('fieldset').hasClass('error')).toEqual(false)
Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't align with the test description 🤔 . Can we create a separate test for this?

@danparnella
Copy link
Contributor Author

Closing in favor of #595

@danparnella danparnella deleted the inputs-aria-props branch September 27, 2023 18:56
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.

aria-labelled by for LabeledField
3 participants