Skip to content

Commit 76ccb94

Browse files
committed
fix(button): do not override user provided tabindex
1 parent 92760e8 commit 76ccb94

File tree

2 files changed

+85
-32
lines changed

2 files changed

+85
-32
lines changed

packages/button/src/LionButton.js

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ export class LionButton extends DelegateMixin(SlotMixin(LionLitElement)) {
88
type: Boolean,
99
reflect: true,
1010
},
11+
role: {
12+
type: String,
13+
reflect: true,
14+
},
15+
tabindex: {
16+
type: Number,
17+
reflect: true,
18+
},
1119
};
1220
}
1321

@@ -93,10 +101,10 @@ export class LionButton extends DelegateMixin(SlotMixin(LionLitElement)) {
93101
];
94102
}
95103

96-
update(changedProperties) {
97-
super.update(changedProperties);
98-
if (changedProperties.has('disabled')) {
99-
this.__onDisabledChanged();
104+
_requestUpdate(name, oldValue) {
105+
super._requestUpdate(name, oldValue);
106+
if (name === 'disabled') {
107+
this.__onDisabledChanged(oldValue);
100108
}
101109
}
102110

@@ -125,12 +133,13 @@ export class LionButton extends DelegateMixin(SlotMixin(LionLitElement)) {
125133
constructor() {
126134
super();
127135
this.disabled = false;
136+
this.role = 'button';
137+
this.tabindex = 0;
128138
this.__keydownDelegationHandler = this.__keydownDelegationHandler.bind(this);
129139
}
130140

131141
connectedCallback() {
132142
super.connectedCallback();
133-
this.__setupA11y();
134143
this.__setupKeydownDelegation();
135144
}
136145

@@ -144,11 +153,6 @@ export class LionButton extends DelegateMixin(SlotMixin(LionLitElement)) {
144153
this.$$slot('_button').click();
145154
}
146155

147-
__setupA11y() {
148-
this.setAttribute('role', 'button');
149-
this.setAttribute('tabindex', this.disabled ? -1 : 0);
150-
}
151-
152156
__setupKeydownDelegation() {
153157
this.addEventListener('keydown', this.__keydownDelegationHandler);
154158
}
@@ -167,6 +171,11 @@ export class LionButton extends DelegateMixin(SlotMixin(LionLitElement)) {
167171
}
168172

169173
__onDisabledChanged() {
170-
this.setAttribute('tabindex', this.disabled ? -1 : 0);
174+
if (this.disabled) {
175+
this.__originalTabIndex = this.tabindex;
176+
this.tabindex = -1;
177+
} else {
178+
this.tabindex = this.__originalTabIndex;
179+
}
171180
}
172181
}

packages/button/test/lion-button.test.js

Lines changed: 65 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,43 +6,87 @@ import '../lion-button.js';
66

77
describe('lion-button', () => {
88
it('behaves like native `button` in terms of a11y', async () => {
9-
const lionButton = await fixture(`<lion-button>foo</lion-button>`);
10-
expect(lionButton.getAttribute('role')).to.equal('button');
11-
expect(lionButton.getAttribute('tabindex')).to.equal('0');
9+
const el = await fixture(`<lion-button>foo</lion-button>`);
10+
expect(el.getAttribute('role')).to.equal('button');
11+
expect(el.getAttribute('tabindex')).to.equal('0');
1212
});
1313

1414
it('has no type by default on the native button', async () => {
15-
const lionButton = await fixture(`<lion-button>foo</lion-button>`);
16-
const nativeButton = lionButton.$$slot('_button');
15+
const el = await fixture(`<lion-button>foo</lion-button>`);
16+
const nativeButton = el.$$slot('_button');
1717
expect(nativeButton.getAttribute('type')).to.be.null;
1818
});
1919

2020
it('has type="submit" on the native button when set', async () => {
21-
const lionButton = await fixture(`<lion-button type="submit">foo</lion-button>`);
22-
const nativeButton = lionButton.$$slot('_button');
21+
const el = await fixture(`<lion-button type="submit">foo</lion-button>`);
22+
const nativeButton = el.$$slot('_button');
2323
expect(nativeButton.getAttribute('type')).to.equal('submit');
2424
});
2525

2626
it('hides the native button in the UI', async () => {
27-
const lionButton = await fixture(`<lion-button>foo</lion-button>`);
28-
const nativeButton = lionButton.$$slot('_button');
27+
const el = await fixture(`<lion-button>foo</lion-button>`);
28+
const nativeButton = el.$$slot('_button');
2929
expect(nativeButton.getAttribute('tabindex')).to.equal('-1');
3030
expect(window.getComputedStyle(nativeButton).visibility).to.equal('hidden');
3131
});
3232

3333
it('can be disabled imperatively', async () => {
34-
const lionButton = await fixture(`<lion-button disabled>foo</lion-button>`);
35-
expect(lionButton.getAttribute('tabindex')).to.equal('-1');
36-
37-
lionButton.disabled = false;
38-
await lionButton.updateComplete;
39-
expect(lionButton.getAttribute('tabindex')).to.equal('0');
40-
expect(lionButton.hasAttribute('disabled')).to.equal(false);
41-
42-
lionButton.disabled = true;
43-
await lionButton.updateComplete;
44-
expect(lionButton.getAttribute('tabindex')).to.equal('-1');
45-
expect(lionButton.hasAttribute('disabled')).to.equal(true);
34+
const el = await fixture(`<lion-button disabled>foo</lion-button>`);
35+
expect(el.getAttribute('tabindex')).to.equal('-1');
36+
37+
el.disabled = false;
38+
await el.updateComplete;
39+
expect(el.getAttribute('tabindex')).to.equal('0');
40+
expect(el.hasAttribute('disabled')).to.equal(false);
41+
42+
el.disabled = true;
43+
await el.updateComplete;
44+
expect(el.getAttribute('tabindex')).to.equal('-1');
45+
expect(el.hasAttribute('disabled')).to.equal(true);
46+
});
47+
48+
describe('a11y', () => {
49+
it('has a role="button" by default', async () => {
50+
const el = await fixture(`<lion-button>foo</lion-button>`);
51+
expect(el.getAttribute('role')).to.equal('button');
52+
el.role = 'foo';
53+
await el.updateComplete;
54+
expect(el.getAttribute('role')).to.equal('foo');
55+
});
56+
57+
it('does not override user provided role', async () => {
58+
const el = await fixture(`<lion-button role="foo">foo</lion-button>`);
59+
expect(el.getAttribute('role')).to.equal('foo');
60+
});
61+
62+
it('has a tabindex="0" by default', async () => {
63+
const el = await fixture(`<lion-button>foo</lion-button>`);
64+
expect(el.getAttribute('tabindex')).to.equal('0');
65+
});
66+
67+
it('has a tabindex="-1" when disabled', async () => {
68+
const el = await fixture(`<lion-button disabled>foo</lion-button>`);
69+
expect(el.getAttribute('tabindex')).to.equal('-1');
70+
el.disabled = false;
71+
await el.updateComplete;
72+
expect(el.getAttribute('tabindex')).to.equal('0');
73+
el.disabled = true;
74+
await el.updateComplete;
75+
expect(el.getAttribute('tabindex')).to.equal('-1');
76+
});
77+
78+
it('does not override user provided tabindex', async () => {
79+
const el = await fixture(`<lion-button tabindex="5">foo</lion-button>`);
80+
expect(el.getAttribute('tabindex')).to.equal('5');
81+
});
82+
83+
it('disabled does not override user provided tabindex', async () => {
84+
const el = await fixture(`<lion-button tabindex="5" disabled>foo</lion-button>`);
85+
expect(el.getAttribute('tabindex')).to.equal('-1');
86+
el.disabled = false;
87+
await el.updateComplete;
88+
expect(el.getAttribute('tabindex')).to.equal('5');
89+
});
4690
});
4791

4892
describe('form integration', () => {

0 commit comments

Comments
 (0)