-
Notifications
You must be signed in to change notification settings - Fork 9k
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
Update authorization button semantics to improve accessibility #5298
base: master
Are you sure you want to change the base?
Update authorization button semantics to improve accessibility #5298
Conversation
b4b8ac9
to
af0d057
Compare
@@ -37,7 +37,8 @@ export default class OperationSummaryPath extends PureComponent{ | |||
const DeepLink = getComponent( "DeepLink" ) | |||
|
|||
return( | |||
<span className={ deprecated ? "opblock-summary-path__deprecated" : "opblock-summary-path" } | |||
<span className={ deprecated ? "opblock-summary-path__deprecated" : "opblock-summary-path" } | |||
id={`${tag}-${operationId}`} |
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.
I'm assuming this ID will be unique, but as I mentioned in the PR summary it would be safer to include a uuid package to generate unique IDs. If that seems like a good idea, I would be happy to update this PR with that change
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.
Change request: use path + method
instead of tag + operationId here.
Combinations of tag and operationId are not technically guaranteed to be unique, although the OpenAPI spec says non-unique operationIds are illegal. We've had complaints from users in places where we track state by operationId (e.g. #5096). You can also see this in action on the LA Metro Arrivals API - click on /agencies/{agency_tag}/routes/
, notice that more than one operation expands (I've been meaning to send them some revisions to their document...).
Fortunately, because (a) paths and methods in OpenAPI documents are key names, and (b) YAML/JSON doesn't allow duplicated key names, path + method
is guaranteed to be unique.
This is another instance of accessibility features not being safe for use cases that include multiple Swagger UI instances on one page though - see my thoughts on that in #5299 (comment).
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.
FYI: you should be able to pull method
out of operationProps.toJS()
.
af0d057
to
785a430
Compare
aria-label={isAuthorized ? "authorization button locked" : "authorization button unlocked"} | ||
aria-describedBy={describedBy ? describedBy : null} | ||
aria-haspopup="dialog" | ||
aria-label={isAuthorized ? "authorize locked" : "authorize unlocked"} |
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.
Admittedly I find the UX of the authorisation stuff a bit unclear but as I understand it we get the "Locked" state when you have put in authentication credentials that the route requires. is that right?
If so, I wonder whether locked/unlocked is the right thing to read out here? Since the thing you’re checking is whether it is authorised I wonder whether just "Authorised"/"Unauthorised" would be better labels for this?
Having said that I might just be misunderstanding the logic for these padlock states
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.
I'm actually not sure -- I was half-hoping someone more familiar with Swagger UI would comment and suggest better wording. Absent that, I just went with locked/unlocked since it was used before and it gave the closest parity with the visual experience.
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 based on the actions in the popup, "Authorize" and "Remove authorization" might fit best?
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.
@leggsimon: Having said that I might just be misunderstanding the logic for these padlock states
You wouldn't be the first - just take a look at #4402! We're moving away from the lock metaphor in general, it's clear it has not been a hit with users.
I can't share the new design proposal publicly yet, but suffice it to say that it unambiguously communicates two states: "authorization is available for this request, and has
/but has not
been provided".
I see no reason to torture screen reader users with a visual metaphor that even visual users don't like!
Change request: let's change this label!
@smhigley, when we last chatted I don't think we quite landed on an decision for what aria-label
should represent in the context of a button that both conveys current state and triggers a dialog that allows users to mutate state - should the label describe the current state or what the button allows the user to change?
If we're focusing on current state, I think something like Authorization is active
and Authorization is available but not active
makes sense, but we can be more terse if that's conventional for labels, e.g. Authorization active
and Authorization available
.
Conversely, if we're focusing on mutations that the user can trigger, your suggestion of Authorize
and Remove authorization
is good.
Which road to take is up to you, @smhigley!
<span>Authorize</span> | ||
<svg width="20" height="20"> | ||
<svg width="20" height="20" aria-labelledby="authorize-button" role="img"> | ||
<title id="authorize-button">{isAuthorized ? "Locked" : "Unlocked"}</title> |
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.
Similar thoughts to the other comment about the wording of 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.
Indeed, let's change the wording here as well.
@@ -73,6 +73,7 @@ export default class OperationSummary extends PureComponent { | |||
{ | |||
(!security || !security.count()) ? null : | |||
<AuthorizeOperationBtn | |||
describedBy={`${tag}-${operationId}`} |
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.
love this! although I think there should maybe be a test that ensures that the id in the operation summary path and the describedBy value here should be the same. maybe by passing it as a prop down somehow. I just worry that having the same thing done in two places might break at some point in the future if someone fiddles with the id in the operation summary path.
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.
@leggsimon I worried about the same thing. My ideal solution would actually be a uuid library, but I wanted feedback from project admins before including one in a first PR 😄. Unit tests, at least for this, seem like a good addition though 👍
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.
Unit tests would be great, and then I think rather than doing the id attribute creation in both places it might be nice to define the value outside this return
jsx and pass it as an id
prop to OperationSummaryPath
on line 63 maybe?
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.
But yes, I would be in favour of a uuid I think. Unfortunately I can’t make those calls either 😄
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.
Change request: use path + method instead of tag + operationId here.
Same idea as above (https://github.com/swagger-api/swagger-ui/pull/5298/files#r280963958).
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.
Also, a test would be good for the ID - I think it could fit well in either the Enzyme (component-focused) tests or the Cypress (real DOM end-to-end) tests.
Where are we with this PR? Who can resolve the conflicts? |
Resolves #5297
Description
Something worth noting: I created element IDs that should be unique, but are not guaranteed to be. Since accessibility features often rely on id refs, it might be a good idea to include a uuid package to generate unique IDs. I would be happy to do so in this PR if the project maintainers agree.
Motivation and Context
Improves experience for screen reader users (and potentially other assistive tech users).
How Has This Been Tested?
Tested in NVDA/Firefox, NVDA/Chrome, JAWS/Chrome, Narrator/Edge, and iOS/Safari/VoiceOver
Checklist
My PR contains...
src/
is unmodified: changes to documentation, CI, metadata, etc.)package.json
)My changes...
Documentation
Automated tests
(I did not find existing tests for the components in question, so I didn't add new test files. As this change introduces no change in functionality it didn't seem pressing, but I would also be happy to add tests if that would improve the PR).