@instructure/ui-buttons 10.15.1 → 10.15.2-snapshot-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [10.15.2-snapshot-0](https://github.com/instructure/instructure-ui/compare/v10.15.1...v10.15.2-snapshot-0) (2025-04-04)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * **ui-buttons:** fix tabindex=0 added unnecessarly to Buttons ([a9a68a4](https://github.com/instructure/instructure-ui/commit/a9a68a447d04e6c5509e0ce785745cdcaea73c54))
12
+
13
+
14
+
15
+
16
+
6
17
  ## [10.15.1](https://github.com/instructure/instructure-ui/compare/v10.15.0...v10.15.1) (2025-04-03)
7
18
 
8
19
  **Note:** Version bump only for package @instructure/ui-buttons
@@ -105,6 +105,26 @@ describe('<BaseButton/>', () => {
105
105
  expect(button).toBeInTheDocument();
106
106
  expect(button).toHaveAttribute('tabIndex', '0');
107
107
  });
108
+ it('should not set tabIndex="0" when the element has it by default', () => {
109
+ const onClick = vi.fn();
110
+ render(/*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(BaseButton, {
111
+ as: "button",
112
+ onClick: onClick
113
+ }, "Hello Button"), /*#__PURE__*/React.createElement(BaseButton, {
114
+ onClick: onClick,
115
+ href: "example.html"
116
+ }, "Hello link")));
117
+ const button = screen.getByRole('button', {
118
+ name: 'Hello Button'
119
+ });
120
+ expect(button).toBeInTheDocument();
121
+ expect(button).not.toHaveAttribute('tabIndex');
122
+ const link = screen.getByRole('link', {
123
+ name: 'Hello link'
124
+ });
125
+ expect(link).toBeInTheDocument();
126
+ expect(link).not.toHaveAttribute('tabIndex');
127
+ });
108
128
  it('should pass down the type prop to the button element', async () => {
109
129
  const onClick = vi.fn();
110
130
  render(/*#__PURE__*/React.createElement(BaseButton, {
@@ -137,7 +157,7 @@ describe('<BaseButton/>', () => {
137
157
  button.focus();
138
158
  expect(document.activeElement).toBe(button);
139
159
  });
140
- it('should provide an elementRef prop', async () => {
160
+ it('should provide an elementRef prop', async () => {
141
161
  const elementRef = vi.fn();
142
162
  render(/*#__PURE__*/React.createElement(BaseButton, {
143
163
  elementRef: elementRef
@@ -208,9 +208,25 @@ let BaseButton = (_dec = withStyle(generateStyles, generateComponentTheme), _dec
208
208
  props = _objectWithoutProperties(_this$props7, _excluded);
209
209
  const isDisabled = this.isDisabled,
210
210
  isEnabled = this.isEnabled,
211
- isReadOnly = this.isReadOnly;
211
+ isReadOnly = this.isReadOnly,
212
+ elementType = this.elementType;
213
+ // only add 0 tabIndex value if it doesn't have it by default, see
214
+ // https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex
215
+ let needsZeroTabIndex = true;
216
+ if (typeof elementType == 'string') {
217
+ if (['button', 'frame', 'iframe', 'input', 'object', 'select', 'textarea', 'summary'].includes(elementType)) {
218
+ needsZeroTabIndex = false;
219
+ }
220
+ if (href && (elementType === 'a' || elementType === 'area')) {
221
+ needsZeroTabIndex = false;
222
+ }
223
+ }
224
+ let tabIndexValue = tabIndex;
225
+ if (onClick && as && needsZeroTabIndex) {
226
+ tabIndexValue = tabIndex || 0;
227
+ }
212
228
  return jsx(View, Object.assign({}, passthroughProps(props), {
213
- as: this.elementType,
229
+ as: elementType,
214
230
  focusColor: this.focusColor,
215
231
  position: "relative",
216
232
  display: display,
@@ -227,7 +243,7 @@ let BaseButton = (_dec = withStyle(generateStyles, generateComponentTheme), _dec
227
243
  onClick: this.handleClick,
228
244
  onKeyDown: this.handleKeyDown,
229
245
  role: onClick && as !== 'button' ? 'button' : void 0,
230
- tabIndex: onClick && as ? tabIndex || 0 : tabIndex,
246
+ tabIndex: tabIndexValue,
231
247
  disabled: isDisabled || isReadOnly,
232
248
  css: isEnabled ? styles === null || styles === void 0 ? void 0 : styles.baseButton : null,
233
249
  focusRingBorderRadius: String(styles === null || styles === void 0 ? void 0 : (_styles$content = styles.content) === null || _styles$content === void 0 ? void 0 : _styles$content.borderRadius)
@@ -107,6 +107,26 @@ describe('<BaseButton/>', () => {
107
107
  expect(button).toBeInTheDocument();
108
108
  expect(button).toHaveAttribute('tabIndex', '0');
109
109
  });
110
+ it('should not set tabIndex="0" when the element has it by default', () => {
111
+ const onClick = _vitest.vi.fn();
112
+ (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_index.BaseButton, {
113
+ as: "button",
114
+ onClick: onClick
115
+ }, "Hello Button"), /*#__PURE__*/_react.default.createElement(_index.BaseButton, {
116
+ onClick: onClick,
117
+ href: "example.html"
118
+ }, "Hello link")));
119
+ const button = _react2.screen.getByRole('button', {
120
+ name: 'Hello Button'
121
+ });
122
+ expect(button).toBeInTheDocument();
123
+ expect(button).not.toHaveAttribute('tabIndex');
124
+ const link = _react2.screen.getByRole('link', {
125
+ name: 'Hello link'
126
+ });
127
+ expect(link).toBeInTheDocument();
128
+ expect(link).not.toHaveAttribute('tabIndex');
129
+ });
110
130
  it('should pass down the type prop to the button element', async () => {
111
131
  const onClick = _vitest.vi.fn();
112
132
  (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_index.BaseButton, {
@@ -139,7 +159,7 @@ describe('<BaseButton/>', () => {
139
159
  button.focus();
140
160
  expect(document.activeElement).toBe(button);
141
161
  });
142
- it('should provide an elementRef prop', async () => {
162
+ it('should provide an elementRef prop', async () => {
143
163
  const elementRef = _vitest.vi.fn();
144
164
  (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_index.BaseButton, {
145
165
  elementRef: elementRef
@@ -218,9 +218,25 @@ let BaseButton = exports.BaseButton = (_dec = (0, _emotion.withStyle)(_styles.de
218
218
  props = (0, _objectWithoutProperties2.default)(_this$props7, _excluded);
219
219
  const isDisabled = this.isDisabled,
220
220
  isEnabled = this.isEnabled,
221
- isReadOnly = this.isReadOnly;
221
+ isReadOnly = this.isReadOnly,
222
+ elementType = this.elementType;
223
+ // only add 0 tabIndex value if it doesn't have it by default, see
224
+ // https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex
225
+ let needsZeroTabIndex = true;
226
+ if (typeof elementType == 'string') {
227
+ if (['button', 'frame', 'iframe', 'input', 'object', 'select', 'textarea', 'summary'].includes(elementType)) {
228
+ needsZeroTabIndex = false;
229
+ }
230
+ if (href && (elementType === 'a' || elementType === 'area')) {
231
+ needsZeroTabIndex = false;
232
+ }
233
+ }
234
+ let tabIndexValue = tabIndex;
235
+ if (onClick && as && needsZeroTabIndex) {
236
+ tabIndexValue = tabIndex || 0;
237
+ }
222
238
  return (0, _emotion.jsx)(_View.View, Object.assign({}, (0, _passthroughProps.passthroughProps)(props), {
223
- as: this.elementType,
239
+ as: elementType,
224
240
  focusColor: this.focusColor,
225
241
  position: "relative",
226
242
  display: display,
@@ -237,7 +253,7 @@ let BaseButton = exports.BaseButton = (_dec = (0, _emotion.withStyle)(_styles.de
237
253
  onClick: this.handleClick,
238
254
  onKeyDown: this.handleKeyDown,
239
255
  role: onClick && as !== 'button' ? 'button' : void 0,
240
- tabIndex: onClick && as ? tabIndex || 0 : tabIndex,
256
+ tabIndex: tabIndexValue,
241
257
  disabled: isDisabled || isReadOnly,
242
258
  css: isEnabled ? styles === null || styles === void 0 ? void 0 : styles.baseButton : null,
243
259
  focusRingBorderRadius: String(styles === null || styles === void 0 ? void 0 : (_styles$content = styles.content) === null || _styles$content === void 0 ? void 0 : _styles$content.borderRadius)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instructure/ui-buttons",
3
- "version": "10.15.1",
3
+ "version": "10.15.2-snapshot-0",
4
4
  "description": "Accessible button components",
5
5
  "author": "Instructure, Inc. Engineering and Product Design",
6
6
  "module": "./es/index.js",
@@ -23,10 +23,10 @@
23
23
  },
24
24
  "license": "MIT",
25
25
  "devDependencies": {
26
- "@instructure/ui-axe-check": "10.15.1",
27
- "@instructure/ui-babel-preset": "10.15.1",
28
- "@instructure/ui-test-utils": "10.15.1",
29
- "@instructure/ui-themes": "10.15.1",
26
+ "@instructure/ui-axe-check": "10.15.2-snapshot-0",
27
+ "@instructure/ui-babel-preset": "10.15.2-snapshot-0",
28
+ "@instructure/ui-test-utils": "10.15.2-snapshot-0",
29
+ "@instructure/ui-themes": "10.15.2-snapshot-0",
30
30
  "@testing-library/jest-dom": "^6.6.3",
31
31
  "@testing-library/react": "^16.0.1",
32
32
  "@testing-library/user-event": "^14.5.2",
@@ -34,21 +34,21 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@babel/runtime": "^7.26.0",
37
- "@instructure/console": "10.15.1",
38
- "@instructure/emotion": "10.15.1",
39
- "@instructure/shared-types": "10.15.1",
40
- "@instructure/ui-a11y-content": "10.15.1",
41
- "@instructure/ui-a11y-utils": "10.15.1",
42
- "@instructure/ui-color-utils": "10.15.1",
43
- "@instructure/ui-dom-utils": "10.15.1",
44
- "@instructure/ui-icons": "10.15.1",
45
- "@instructure/ui-position": "10.15.1",
46
- "@instructure/ui-prop-types": "10.15.1",
47
- "@instructure/ui-react-utils": "10.15.1",
48
- "@instructure/ui-testable": "10.15.1",
49
- "@instructure/ui-tooltip": "10.15.1",
50
- "@instructure/ui-utils": "10.15.1",
51
- "@instructure/ui-view": "10.15.1",
37
+ "@instructure/console": "10.15.2-snapshot-0",
38
+ "@instructure/emotion": "10.15.2-snapshot-0",
39
+ "@instructure/shared-types": "10.15.2-snapshot-0",
40
+ "@instructure/ui-a11y-content": "10.15.2-snapshot-0",
41
+ "@instructure/ui-a11y-utils": "10.15.2-snapshot-0",
42
+ "@instructure/ui-color-utils": "10.15.2-snapshot-0",
43
+ "@instructure/ui-dom-utils": "10.15.2-snapshot-0",
44
+ "@instructure/ui-icons": "10.15.2-snapshot-0",
45
+ "@instructure/ui-position": "10.15.2-snapshot-0",
46
+ "@instructure/ui-prop-types": "10.15.2-snapshot-0",
47
+ "@instructure/ui-react-utils": "10.15.2-snapshot-0",
48
+ "@instructure/ui-testable": "10.15.2-snapshot-0",
49
+ "@instructure/ui-tooltip": "10.15.2-snapshot-0",
50
+ "@instructure/ui-utils": "10.15.2-snapshot-0",
51
+ "@instructure/ui-view": "10.15.2-snapshot-0",
52
52
  "keycode": "^2",
53
53
  "prop-types": "^15.8.1"
54
54
  },
@@ -126,6 +126,27 @@ describe('<BaseButton/>', () => {
126
126
  expect(button).toHaveAttribute('tabIndex', '0')
127
127
  })
128
128
 
129
+ it('should not set tabIndex="0" when the element has it by default', () => {
130
+ const onClick = vi.fn()
131
+
132
+ render(
133
+ <>
134
+ <BaseButton as="button" onClick={onClick}>
135
+ Hello Button
136
+ </BaseButton>
137
+ <BaseButton onClick={onClick} href="example.html">
138
+ Hello link
139
+ </BaseButton>
140
+ </>
141
+ )
142
+ const button = screen.getByRole('button', { name: 'Hello Button' })
143
+ expect(button).toBeInTheDocument()
144
+ expect(button).not.toHaveAttribute('tabIndex')
145
+ const link = screen.getByRole('link', { name: 'Hello link' })
146
+ expect(link).toBeInTheDocument()
147
+ expect(link).not.toHaveAttribute('tabIndex')
148
+ })
149
+
129
150
  it('should pass down the type prop to the button element', async () => {
130
151
  const onClick = vi.fn()
131
152
  render(
@@ -163,7 +184,7 @@ describe('<BaseButton/>', () => {
163
184
  expect(document.activeElement).toBe(button)
164
185
  })
165
186
 
166
- it('should provide an elementRef prop', async () => {
187
+ it('should provide an elementRef prop', async () => {
167
188
  const elementRef = vi.fn()
168
189
  render(<BaseButton elementRef={elementRef}>Hello World</BaseButton>)
169
190
 
@@ -255,12 +255,37 @@ class BaseButton extends Component<BaseButtonProps> {
255
255
  ...props
256
256
  } = this.props
257
257
 
258
- const { isDisabled, isEnabled, isReadOnly } = this
259
-
258
+ const { isDisabled, isEnabled, isReadOnly, elementType } = this
259
+ // only add 0 tabIndex value if it doesn't have it by default, see
260
+ // https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex
261
+ let needsZeroTabIndex = true
262
+ if (typeof elementType == 'string') {
263
+ if (
264
+ [
265
+ 'button',
266
+ 'frame',
267
+ 'iframe',
268
+ 'input',
269
+ 'object',
270
+ 'select',
271
+ 'textarea',
272
+ 'summary'
273
+ ].includes(elementType)
274
+ ) {
275
+ needsZeroTabIndex = false
276
+ }
277
+ if (href && (elementType === 'a' || elementType === 'area')) {
278
+ needsZeroTabIndex = false
279
+ }
280
+ }
281
+ let tabIndexValue = tabIndex
282
+ if (onClick && as && needsZeroTabIndex) {
283
+ tabIndexValue = tabIndex || 0
284
+ }
260
285
  return (
261
286
  <View
262
287
  {...passthroughProps(props)}
263
- as={this.elementType}
288
+ as={elementType}
264
289
  focusColor={this.focusColor}
265
290
  position="relative"
266
291
  display={display}
@@ -277,7 +302,7 @@ class BaseButton extends Component<BaseButtonProps> {
277
302
  onClick={this.handleClick}
278
303
  onKeyDown={this.handleKeyDown}
279
304
  role={onClick && as !== 'button' ? 'button' : undefined}
280
- tabIndex={onClick && as ? tabIndex || 0 : tabIndex}
305
+ tabIndex={tabIndexValue}
281
306
  disabled={isDisabled || isReadOnly}
282
307
  css={isEnabled ? styles?.baseButton : null}
283
308
  focusRingBorderRadius={String(