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

Update authorization button semantics to improve accessibility #5298

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

smhigley
Copy link

Resolves #5297

Description

  • Adds aria-haspopup to authorization buttons to communicate existence of dialog
  • Updates the main authorize button to include locked/unlocked in accessible name
  • Hides operation authorize buttons' SVG to prevent screen readers accessing internal svg elements using caret navigation in scan mode
  • Associates operation-specific authorization buttons with the name of the operation they control through aria-describedby

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

  • No code changes (src/ is unmodified: changes to documentation, CI, metadata, etc.)
  • Dependency changes (any modification to dependencies in package.json)
  • Bug fixes (non-breaking change which fixes an issue)
  • Improvements (misc. changes to existing features)
  • Features (non-breaking change which adds functionality)

My changes...

  • are breaking changes to a public API (config options, System API, major UI change, etc).
  • are breaking changes to a private API (Redux, component props, utility functions, etc.).
  • are breaking changes to a developer API (npm script behavior changes, new dev system dependencies, etc).
  • are not breaking changes.

Documentation

  • My changes do not require a change to the project documentation.
  • My changes require a change to the project documentation.
  • If yes to above: I have updated the documentation accordingly.

Automated tests

  • My changes can not or do not need to be tested.
  • My changes can and should be tested by unit and/or integration tests.
  • If yes to above: I have added tests to cover my changes.
  • If yes to above: I have taken care to cover edge cases in my tests.
  • All new and existing tests passed.
    (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).

@smhigley smhigley force-pushed the bug/5297-authorization-semantics branch from b4b8ac9 to af0d057 Compare April 12, 2019 21:29
@@ -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}`}
Copy link
Author

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

Copy link
Contributor

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

Copy link
Contributor

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().

@smhigley smhigley force-pushed the bug/5297-authorization-semantics branch from af0d057 to 785a430 Compare April 12, 2019 22:11
aria-label={isAuthorized ? "authorization button locked" : "authorization button unlocked"}
aria-describedBy={describedBy ? describedBy : null}
aria-haspopup="dialog"
aria-label={isAuthorized ? "authorize locked" : "authorize unlocked"}
Copy link
Contributor

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

Copy link
Author

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.

Copy link
Author

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?

Copy link
Contributor

Choose a reason for hiding this comment

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

Would be good to get some feedback here @shockey and @webron. What would be the clearer description for what these values mean given you might not have the visual context of the entire page?

Copy link
Contributor

@shockey shockey May 4, 2019

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>
Copy link
Contributor

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.

Copy link
Contributor

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}`}
Copy link
Contributor

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.

Copy link
Author

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 👍

Copy link
Contributor

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?

Copy link
Contributor

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 😄

Copy link
Contributor

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

Copy link
Contributor

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.

@mgifford
Copy link

Where are we with this PR? Who can resolve the conflicts?

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.

Authorization buttons lack informative semantics
4 participants