-
Notifications
You must be signed in to change notification settings - Fork 638
Add aria attributes to the progress bar #3517
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
Conversation
🦋 Changeset detectedLatest commit: fb50b97 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
size-limit report 📦
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this PR, it is a great improvement! 🙏🏻 I left some comments. Thanks for your time!
src/ProgressBar/ProgressBar.tsx
Outdated
'aria-valuetext': typeof progress === 'string' ? progress : undefined, | ||
'aria-valuemin': 0, | ||
'aria-valuemax': 100, | ||
'aria-busy': progress !== 100, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we consider the string type here as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@broccolinisoup Thanks for bringing this up. I ended up changing aria attributes a bit since t looks like this component really only works if progress is a number or a string of a number like ("66" or "100") Because the string is really just a number using aria-valuetext
isn't actually necessary.
To reduce confusion maybe we should only allow a number passed in?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From the first look at the usages at dotcom, in practicality it seems fine but technically narrowing types are considered as major
change. (reference) And I don't know if there is any historical reasons to include string
type in the first place. Sorry @kendallgassner we are dealing with a bit of an old component here I guess and lots to improve - I am trying to keep my review only specific to your changes but seems like some overlaps are inevitable 😬 I'll post internally as well to bring some more reviewers to get more context on this!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We also have ProgressBar with multiple items like here and we don't get aria attributes in these I realised. There is no usage of ProgressBar.Item
though at dotcom so not sure if it would worth the effort. What do you think??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch!!
To deal with multiple I now throw an error if children are passed in without aria-valuenow
or 'aria-valuetext
:
throw new Error(
'Use the `aria-valuenow` or `aria-valuetext` so screen reader users can determine the `progress`.',
)
What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I see! I have a different take on this but I am not 100% sure, let me know what you think!
Seems like we are handling these aria attributes internally and I think it is a tad inconsistent to ask this from consumers when there are ProgressBar.Item
children.
I am thinking maybe we would want to calculate the total progress internally and update the aria attributes as we do for the single progress item? Something along those lines (not a complete code)
if (children) {
let totalProgress = 0
React.Children.toArray(children).forEach(child => {
if (React.isValidElement(child) && child.props.progress) {
totalProgress += parseInt(child.props.progress)
}
})
}
And use that totalProgress
to calculate the value of aria attributes.
cc @siddharthkp @joshblack I would love their opinion before we decide anything concrete.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi!
We could definitely loop through the children if the usage is predictable. However...
I couldn't find any usage of ProgressBar with multiple items, so we might be able to slide it under the rug and deprecate it safely without any remediation :)
Co-authored-by: Armağan <[email protected]>
Note: Because of |
src/ProgressBar/ProgressBar.tsx
Outdated
type StyledProgressContainerProps = { | ||
inline?: boolean | ||
barSize?: keyof typeof sizeMap | ||
tabIndex?: number |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if you need to add this explicitly 🤔
If you pass tabIndex to ProgressBar, it passes it already passes it down to the html element
<Progress tabIndex={0} progress={50} aria-label="Task completion" />
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Amazing! I'll try this out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this could be an option?
type StyledProgressContainerProps = {
inline?: boolean
barSize?: keyof typeof sizeMap
} & WidthProps &
SxProp &
React.HTMLAttributes<HTMLSpanElement>
const progressAsNumber = typeof progress === 'string' ? parseInt(progress) : progress | ||
|
||
const ariaAttributes = { | ||
'aria-valuenow': progressAsNumber ? Math.round(progressAsNumber) : undefined, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'aria-valuenow': progressAsNumber ? Math.round(progressAsNumber) : undefined, | |
'aria-valuenow': progressAsNumber || undefined, |
Don't think we need to Math.round
because parseInt
already assumes base 10 and returns an integer
parseInt(20.5) === parseInt(20.5, 10) === 20
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh good point! I added the math.round before I used parseInt
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh wait... I need it in the case that the typeof the value is a number
not a string
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I see! My bad!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@siddharthkp one note with parseInt
, I believe it will infer the base if no radix
is provided based on: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt#radix
If 0 or not provided, the radix will be inferred based on string's value. Be careful — this does not always default to 10!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh wow, good note!
I think this would be a good idea to add as a prop so it's surfaced better, especially in the documentation! I'm assuming that if it's a required prop then each implementation would need to be remediated to include a label. But we'd want to include the label either way as AXE will surely throw an error if one isn't included 😕. I do think that it would be beneficial to add it as a required prop, as the usage of the component seems to be limited across dotcom (for now). But would love additional thoughts on this @siddharthkp @broccolinisoup as y'all have more knowledge of the implications here. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks so much for making this PR! 🥳 Just left a couple of comments/questions
const progressAsNumber = typeof progress === 'string' ? parseInt(progress) : progress | ||
|
||
const ariaAttributes = { | ||
'aria-valuenow': progressAsNumber ? Math.round(progressAsNumber) : undefined, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@siddharthkp one note with parseInt
, I believe it will infer the base if no radix
is provided based on: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt#radix
If 0 or not provided, the radix will be inferred based on string's value. Be careful — this does not always default to 10!
src/ProgressBar/ProgressBar.tsx
Outdated
type StyledProgressContainerProps = { | ||
inline?: boolean | ||
barSize?: keyof typeof sizeMap | ||
tabIndex?: number |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this could be an option?
type StyledProgressContainerProps = {
inline?: boolean
barSize?: keyof typeof sizeMap
} & WidthProps &
SxProp &
React.HTMLAttributes<HTMLSpanElement>
</ProgressContainer> | ||
) | ||
} | ||
export type ProgressBarProps = React.HTMLAttributes<HTMLSpanElement> & {bg?: string} & StyledProgressContainerProps & |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@joshblack I added React.HTMLAttributes<HTMLSpanElement>
here!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for doing this! 🥳
I appreciated your comment about the aria-label
above btw, I think our ideal for this stuff should be to move it into the type system but I think that would be considered a breaking change 😞 The approach here seems like a great incremental step and I'll make sure to capture this over in: https://github.com/github/primer/issues/1715 for v37!
FYI, I pulled this comment out into an additional action item: #3605 |
What
During passion week we got to play around with the ProgressBar component and we noticed that this component was missing some aria-attributes. I wanted to create a PR proposing adding these attributes to the component to improve accessibility.
I also added a prop
tabIndex
so the progressBar could be focused if a feature wanted to move focus to the component -- we found this to be essential when working on fileUpload guidance.Screenshots
Please provide before/after screenshots for any visual changes
Merge checklist
Take a look at the What we look for in reviews section of the contributing guidelines for more information on how we review PRs.