forked from react-bootstrap/react-bootstrap
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[fixed] Keyboard accessibility for anchors serving as buttons
Bootstrap uses a lot of styling that is specifically targeting anchor tags that may also serve in the capacity as a button. Unfortunately since Bootstrap does not style said buttons we have to use an anchor tag instead. But in order to maintain keyboard functionality for accessibility concerns even those anchor tags must provide an href. The solution is to add an internal `SafeAnchor` component which ensures that something exists for the href attribute, and calls `event.preventDefault()` when the anchor is clicked. It will then continue to invoke any additional `onClick` handler provided.
- Loading branch information
Showing
11 changed files
with
157 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import React from 'react'; | ||
|
||
/** | ||
* Note: This is intended as a stop-gap for accessibility concerns that the | ||
* Bootstrap CSS does not address as they have styled anchors and not buttons | ||
* in many cases. | ||
*/ | ||
export default class SafeAnchor extends React.Component { | ||
constructor(props) { | ||
super(props); | ||
|
||
this.handleClick = this.handleClick.bind(this); | ||
} | ||
|
||
handleClick(event) { | ||
if (this.props.href === undefined) { | ||
event.preventDefault(); | ||
} | ||
|
||
if (this.props.onClick) { | ||
this.props.onClick(event); | ||
} | ||
} | ||
|
||
render() { | ||
return ( | ||
<a role={this.props.href ? undefined : 'button'} | ||
{...this.props} | ||
onClick={this.handleClick} | ||
href={this.props.href || ''}/> | ||
); | ||
} | ||
} | ||
|
||
SafeAnchor.propTypes = { | ||
href: React.PropTypes.string, | ||
onClick: React.PropTypes.func | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import React from 'react'; | ||
import ReactTestUtils from 'react/lib/ReactTestUtils'; | ||
import SafeAnchor from '../src/SafeAnchor'; | ||
|
||
describe('SafeAnchor', function() { | ||
it('renders an anchor tag', function() { | ||
const instance = ReactTestUtils.renderIntoDocument(<SafeAnchor />); | ||
const node = React.findDOMNode(instance); | ||
|
||
node.tagName.should.equal('A'); | ||
}); | ||
|
||
it('forwards arbitrary props to the anchor', function() { | ||
const instance = ReactTestUtils.renderIntoDocument(<SafeAnchor herpa='derpa' />); | ||
const anchor = ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'A'); | ||
|
||
anchor.props.herpa.should.equal('derpa'); | ||
}); | ||
|
||
it('forwards provided href', function() { | ||
const instance = ReactTestUtils.renderIntoDocument(<SafeAnchor href='http://google.com' />); | ||
const anchor = ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'A'); | ||
|
||
anchor.props.href.should.equal('http://google.com'); | ||
}); | ||
|
||
it('ensures that an href is provided', function() { | ||
const instance = ReactTestUtils.renderIntoDocument(<SafeAnchor />); | ||
const anchor = ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'A'); | ||
|
||
anchor.props.href.should.equal(''); | ||
}); | ||
|
||
it('forwards onClick handler', function(done) { | ||
const handleClick = (event) => { | ||
done(); | ||
}; | ||
const instance = ReactTestUtils.renderIntoDocument(<SafeAnchor onClick={handleClick} />); | ||
const anchor = ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'A'); | ||
|
||
ReactTestUtils.Simulate.click(anchor); | ||
}); | ||
|
||
it('prevents default when no href is provided', function(done) { | ||
const handleClick = (event) => { | ||
event.defaultPrevented.should.be.true; | ||
done(); | ||
}; | ||
const instance = ReactTestUtils.renderIntoDocument(<SafeAnchor onClick={handleClick} />); | ||
const anchor = ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'A'); | ||
|
||
ReactTestUtils.Simulate.click(anchor); | ||
}); | ||
|
||
it('does not prevent default when href is provided', function(done) { | ||
const handleClick = (event) => { | ||
expect(event.defaultPrevented).to.not.be.ok; | ||
done(); | ||
}; | ||
const instance = ReactTestUtils.renderIntoDocument(<SafeAnchor href='#' onClick={handleClick} />); | ||
const anchor = ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'A'); | ||
|
||
ReactTestUtils.Simulate.click(anchor); | ||
}); | ||
|
||
it('forwards provided role', function () { | ||
const instance = ReactTestUtils.renderIntoDocument(<SafeAnchor role='test' />); | ||
const anchor = ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'A'); | ||
|
||
anchor.props.role.should.equal('test'); | ||
}); | ||
|
||
it('forwards provided role with href', function () { | ||
const instance = ReactTestUtils.renderIntoDocument(<SafeAnchor role='test' href='http://google.com' />); | ||
const anchor = ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'A'); | ||
|
||
anchor.props.role.should.equal('test'); | ||
}); | ||
|
||
it('set role=button with no provided href', function () { | ||
const instance = ReactTestUtils.renderIntoDocument(<SafeAnchor />); | ||
const anchor = ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'A'); | ||
|
||
anchor.props.role.should.equal('button'); | ||
}); | ||
|
||
it('sets no role with provided href', function () { | ||
const instance = ReactTestUtils.renderIntoDocument(<SafeAnchor href='http://google.com' />); | ||
const anchor = ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'A'); | ||
|
||
expect(anchor.props.role).to.be.undefined; | ||
}); | ||
}); |