Skip to content

Commit fc33f85

Browse files
authored
fix(Button): add href="#" when no href provided for anchors (#46)
1 parent 32f21c7 commit fc33f85

File tree

3 files changed

+35
-15
lines changed

3 files changed

+35
-15
lines changed

src/Button.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,22 @@ export function useButtonProps({
8080
}
8181
};
8282

83+
if (tagName === 'a') {
84+
// Ensure there's a href so Enter can trigger anchor button.
85+
href ||= '#';
86+
if (disabled) {
87+
href = undefined;
88+
}
89+
}
90+
8391
return [
8492
{
8593
role: 'button',
8694
// explicitly undefined so that it overrides the props disabled in a spread
8795
// e.g. <Tag {...props} {...hookProps} />
8896
disabled: undefined,
8997
tabIndex: disabled ? undefined : tabIndex,
90-
href: tagName === 'a' && disabled ? undefined : href,
98+
href,
9199
target: tagName === 'a' ? target : undefined,
92100
'aria-disabled': !disabled ? undefined : disabled,
93101
rel: tagName === 'a' ? rel : undefined,

test/AnchorSpec.js renamed to test/AnchorSpec.tsx

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,36 @@
11
import { render, fireEvent } from '@testing-library/react';
2+
import sinon from 'sinon';
3+
import { expect } from 'chai';
4+
25
import Anchor from '../src/Anchor';
36

47
describe('Anchor', () => {
58
it('renders an anchor tag', () => {
69
const { container } = render(<Anchor data-testid="anchor" />);
710

8-
container.firstChild.tagName.should.equal('A');
11+
container.firstElementChild!.tagName.should.equal('A');
912
});
1013

1114
it('forwards provided href', () => {
1215
const { container } = render(<Anchor href="http://google.com" />);
1316

14-
container.firstChild.getAttribute('href').should.equal('http://google.com');
17+
container
18+
.firstElementChild!.getAttribute('href')!
19+
.should.equal('http://google.com');
1520
});
1621

17-
// xit('ensures that an href is provided', () => {
18-
// mount(<Anchor />)
19-
// .getDOMNode()
20-
// .hasAttribute('href').should.be.true;
21-
// });
22+
it('ensures that a href is a hash if none provided', () => {
23+
const { container } = render(<Anchor />);
24+
25+
container.firstElementChild!.getAttribute('href')!.should.equal('#');
26+
});
2227

2328
it('forwards onClick handler', () => {
2429
const handleClick = sinon.spy();
2530

2631
const { container } = render(<Anchor onClick={handleClick} />);
2732

28-
fireEvent.click(container.firstChild);
33+
fireEvent.click(container.firstChild!);
2934

3035
handleClick.should.have.been.calledOnce;
3136
});
@@ -35,7 +40,7 @@ describe('Anchor', () => {
3540

3641
const { container } = render(<Anchor onClick={handleClick} />);
3742

38-
fireEvent.keyDown(container.firstChild, { key: ' ' });
43+
fireEvent.keyDown(container.firstChild!, { key: ' ' });
3944

4045
handleClick.should.have.been.calledOnce;
4146
});
@@ -47,7 +52,7 @@ describe('Anchor', () => {
4752
<Anchor href="http://google.com" onKeyDown={onKeyDownSpy} />,
4853
);
4954

50-
fireEvent.keyDown(container.firstChild, { key: ' ' });
55+
fireEvent.keyDown(container.firstChild!, { key: ' ' });
5156

5257
onKeyDownSpy.should.have.been.calledOnce;
5358
});
@@ -57,11 +62,11 @@ describe('Anchor', () => {
5762

5863
const { container, rerender } = render(<Anchor onClick={handleClick} />);
5964

60-
fireEvent.click(container.firstChild);
65+
fireEvent.click(container.firstChild!);
6166

6267
rerender(<Anchor onClick={handleClick} href="#" />);
6368

64-
fireEvent.click(container.firstChild);
69+
fireEvent.click(container.firstChild!);
6570

6671
expect(handleClick).to.have.been.calledTwice;
6772
expect(handleClick.getCall(0).args[0].isDefaultPrevented()).to.be.true;
@@ -72,7 +77,8 @@ describe('Anchor', () => {
7277
const handleClick = sinon.spy();
7378

7479
fireEvent.click(
75-
render(<Anchor href="#foo" onClick={handleClick} />).container.firstChild,
80+
render(<Anchor href="#foo" onClick={handleClick} />).container
81+
.firstChild!,
7682
);
7783

7884
expect(handleClick).to.have.been.calledOnce;
@@ -101,7 +107,7 @@ describe('Anchor', () => {
101107
expect(
102108
render(
103109
<Anchor href="http://google.com" />,
104-
).container.firstChild.hasAttribute('role'),
110+
).container.firstElementChild!.hasAttribute('role'),
105111
).to.be.false;
106112
});
107113
});

test/ButtonSpec.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,4 +163,10 @@ describe('<Button>', () => {
163163

164164
expect(clickSpy).to.have.not.been.called;
165165
});
166+
167+
it('should render an anchor with # if href not provided', () => {
168+
const { container } = render(<Button as="a">Title</Button>);
169+
170+
container.firstElementChild!.getAttribute('href')!.should.equal('#');
171+
});
166172
});

0 commit comments

Comments
 (0)