diff --git a/.changeset/sixty-nails-juggle.md b/.changeset/sixty-nails-juggle.md
new file mode 100644
index 000000000..e3835d632
--- /dev/null
+++ b/.changeset/sixty-nails-juggle.md
@@ -0,0 +1,5 @@
+---
+'@primer/octicons': minor
+---
+
+Add `tabIndex` prop to React icon components
diff --git a/docs/content/packages/react.mdx b/docs/content/packages/react.mdx
index 64d4973d4..d6d220bcb 100644
--- a/docs/content/packages/react.mdx
+++ b/docs/content/packages/react.mdx
@@ -66,6 +66,24 @@ export default () => (
)
```
+### `tabIndex`
+
+You can add the `tabindex` attribute to an SVG element via the `tabIndex` prop if the SVG element is intended to be interactive.
+`tabIndex` prop also controls the `focusable` attribute of the SVG element which is defined by SVG Tiny 1.2 and only implemented in
+Internet Explorer and Microsoft Edge.
+
+If there is no `tabIndex` prop is present (default behavior), it will set the `focusable` attribute to `false`. This is helpful
+for preventing the decorative SVG from being announced by some specialized assistive technology browsing modes which can get delayed
+while trying to parse the SVG markup.
+
+```js
+// Example usage
+import {PlusIcon} from '@primer/octicons-react'
+export default () => (
+ New Item
+)
+```
+
### Sizes
The `size` prop takes `small`, `medium`, and `large` values that can be used to
diff --git a/lib/octicons_react/__tests__/tree-shaking.test.js b/lib/octicons_react/__tests__/tree-shaking.test.js
index 0d1b8d2f3..b4c2bdec3 100644
--- a/lib/octicons_react/__tests__/tree-shaking.test.js
+++ b/lib/octicons_react/__tests__/tree-shaking.test.js
@@ -50,5 +50,5 @@ test('tree shaking single export', async () => {
})
const bundleSize = Buffer.byteLength(output[0].code.trim()) / 1000
- expect(`${bundleSize}kB`).toMatchInlineSnapshot(`"2.484kB"`)
+ expect(`${bundleSize}kB`).toMatchInlineSnapshot(`"2.595kB"`)
})
diff --git a/lib/octicons_react/src/__tests__/__snapshots__/octicon.js.snap b/lib/octicons_react/src/__tests__/__snapshots__/octicon.js.snap
index bf7fb20e3..74fd31a01 100644
--- a/lib/octicons_react/src/__tests__/__snapshots__/octicon.js.snap
+++ b/lib/octicons_react/src/__tests__/__snapshots__/octicon.js.snap
@@ -5,6 +5,7 @@ exports[`An icon component matches snapshot 1`] = `
aria-hidden="true"
class="octicon octicon-alert"
fill="currentColor"
+ focusable="false"
height="16"
role="img"
style="display: inline-block; user-select: none; vertical-align: text-bottom; overflow: visible;"
diff --git a/lib/octicons_react/src/__tests__/octicon.js b/lib/octicons_react/src/__tests__/octicon.js
index d2cdc044d..642d645e4 100644
--- a/lib/octicons_react/src/__tests__/octicon.js
+++ b/lib/octicons_react/src/__tests__/octicon.js
@@ -69,6 +69,23 @@ describe('An icon component', () => {
expect(container.querySelector('svg')).toHaveAttribute('aria-label', 'icon')
})
+ it('set the focusable prop to false if tabIndex prop is not present', () => {
+ const {container} = render()
+ expect(container.querySelector('svg')).toHaveAttribute('focusable', 'false')
+ })
+
+ it('sets focusable prop to true if tabIndex prop is present and greater than 0', () => {
+ const {container} = render()
+ expect(container.querySelector('svg')).toHaveAttribute('tabindex', '0')
+ expect(container.querySelector('svg')).toHaveAttribute('focusable', 'true')
+ })
+
+ it('sets focusable prop to false if tabIndex prop is -1', () => {
+ const {container} = render()
+ expect(container.querySelector('svg')).toHaveAttribute('tabindex', '-1')
+ expect(container.querySelector('svg')).toHaveAttribute('focusable', 'false')
+ })
+
it('respects the className prop', () => {
const {container} = render()
expect(container.querySelector('svg')).toHaveAttribute('class', 'foo')
diff --git a/lib/octicons_react/src/createIconComponent.js b/lib/octicons_react/src/createIconComponent.js
index 7f08ecb86..75849f0c3 100644
--- a/lib/octicons_react/src/createIconComponent.js
+++ b/lib/octicons_react/src/createIconComponent.js
@@ -10,7 +10,7 @@ export function createIconComponent(name, defaultClassName, getSVGData) {
const svgDataByHeight = getSVGData()
const heights = Object.keys(svgDataByHeight)
- function Icon({'aria-label': ariaLabel, className, fill = 'currentColor', size, verticalAlign}) {
+ function Icon({'aria-label': ariaLabel, tabIndex, className, fill = 'currentColor', size, verticalAlign}) {
const height = sizeMap[size] || size
const naturalHeight = closestNaturalHeight(heights, height)
const naturalWidth = svgDataByHeight[naturalHeight].width
@@ -20,6 +20,8 @@ export function createIconComponent(name, defaultClassName, getSVGData) {
return (