Skip to content

Commit 85caf7a

Browse files
committed
feat: New prop to disable focusing tabs on click
1 parent f849c30 commit 85caf7a

File tree

3 files changed

+52
-18
lines changed

3 files changed

+52
-18
lines changed

README.md

+28-15
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ https://reactcommunity.org/react-tabs/
77
> Supports React 16.8.0 or newer
88
99
<ins><blockquote class="rich-diff-level-zero"> <p class="rich-diff-level-one">react-tabs was tested on real mobile devices and browsers with<br>
10-
<img src="./examples/src/images/Browserstack-logo.svg" height="50" alt="Browserstack">
10+
<img src="./examples/src/images/Browserstack-logo.svg" height="50" alt="Browserstack">
1111

1212
</p> </blockquote></ins>
1313

@@ -16,7 +16,9 @@ https://reactcommunity.org/react-tabs/
1616
```bash
1717
yarn add react-tabs
1818
```
19+
1920
or
21+
2022
```bash
2123
npm install --save react-tabs
2224
```
@@ -78,7 +80,7 @@ react-tabs consists of 4 components which all need to be used together.
7880
7981
If you specify additional props on the `<Tabs />` component they will be forwarded to the rendered `<div />`.
8082
81-
#### className: `string | Array<string> | { [string]: boolean }`
83+
#### className: `string | Array<string> | { [string]: boolean }`
8284
8385
> default: `"react-tabs"`
8486
@@ -130,6 +132,14 @@ Register a callback that will receive the underlying DOM node for every mount. I
130132
131133
If you're rendering `react-tabs` within a different `window` context than the default one; for example, an iframe.
132134
135+
#### focusTabOnClick: `boolean`
136+
137+
> default: `true`
138+
139+
By default the tab that is clicked will also be focused in the DOM. If set to `false` the tab will not be focused anymore.
140+
141+
> Be aware that keyboard navigation will not work after click if set to false. Though one can still focus the tabs by pressing `tab` and then keyboard navigation will work.
142+
133143
#### forceRenderTabPanel: `boolean`
134144
135145
> default: `false`
@@ -178,7 +188,7 @@ Provide a custom class name for the active tab panel.
178188
179189
If you specify additional props on the `<TabList />` component they will be forwarded to the rendered `<ul />`.
180190
181-
#### className: `string | Array<string> | { [string]: boolean }`
191+
#### className: `string | Array<string> | { [string]: boolean }`
182192
183193
> default: `"react-tabs__tab-list"`
184194
@@ -190,7 +200,7 @@ Provide a custom class name for the `<ul />`.
190200
191201
If you specify additional props on the `<Tab />` component they will be forwarded to the rendered `<li />`.
192202
193-
#### className: `string | Array<string> | { [string]: boolean }`
203+
#### className: `string | Array<string> | { [string]: boolean }`
194204
195205
> default: `"react-tabs__tab"`
196206
@@ -230,7 +240,7 @@ Overrides the tabIndex to enabled tabbing between tabs.
230240
231241
If you specify additional props on the `<TabPanel />` component they will be forwarded to the rendered `<div />`.
232242
233-
#### className: `string | Array<string> | { [string]: boolean }`
243+
#### className: `string | Array<string> | { [string]: boolean }`
234244
235245
> default: `"react-tabs__tab-panel"`
236246
@@ -265,7 +275,7 @@ This is the default mode of react-tabs and makes the react-tabs components handl
265275
In this mode you cannot force a tab change during runtime.
266276
267277
```js
268-
<Tabs defaultIndex={1} onSelect={index => console.log(index)}>
278+
<Tabs defaultIndex={1} onSelect={(index) => console.log(index)}>
269279
<TabList>
270280
<Tab>Title 1</Tab>
271281
<Tab>Title 2</Tab>
@@ -288,7 +298,7 @@ const App = () => {
288298
const [tabIndex, setTabIndex] = useState(0);
289299
290300
return (
291-
<Tabs selectedIndex={tabIndex} onSelect={index => setTabIndex(index)}>
301+
<Tabs selectedIndex={tabIndex} onSelect={(index) => setTabIndex(index)}>
292302
<TabList>
293303
<Tab>Title 1</Tab>
294304
<Tab>Title 2</Tab>
@@ -340,33 +350,36 @@ When using the UMD version of react-tabs you can easily use the default styles b
340350
<html>
341351
<head>
342352
...
343-
<link rel="stylesheet" href="https://unpkg.com/react-tabs/style/react-tabs.css">
353+
<link
354+
rel="stylesheet"
355+
href="https://unpkg.com/react-tabs/style/react-tabs.css"
356+
/>
344357
</head>
345358
...
346359
</html>
347360
```
348361
349-
350362
### Custom Style
351363
352364
You can also always just simply copy the default style to your own css/scss/less and modify it to your own needs. The changelog will always tell you when classes change and we also consider changes that break the styling as semver major.
353365
354366
### Custom Components
355367
356368
#### Set `tabsRole`
369+
357370
In case you want to create your own component wrapping the ones that the library provides, you have to set its `tabsRole`. This value is used inside react-tabs to check the role of a component inside `<Tabs />`.
358371
359372
Possible values for tabsRole are:
360-
* Tab
361-
* TabPanel
362-
* TabList
363373
364-
#### Pass through properties
365-
Note: Because of how react-tabs works internally (it uses cloning to opaquely control various parts of the tab state), you need to pass any incoming props to the component you're wrapping. The easiest way to do this is to use the rest and spread operators, e.g. see `{...otherProps}` below.
374+
- Tab
375+
- TabPanel
376+
- TabList
366377
378+
#### Pass through properties
367379
380+
Note: Because of how react-tabs works internally (it uses cloning to opaquely control various parts of the tab state), you need to pass any incoming props to the component you're wrapping. The easiest way to do this is to use the rest and spread operators, e.g. see `{...otherProps}` below.
368381
369-
``` javascript
382+
```javascript
370383
import { Tabs, TabList, Tab, TabPanel } from 'react-tabs';
371384
372385
// All custom elements should pass through other props

src/components/Tabs.js

+9-3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const propTypes = {
2323
disabledTabClassName: PropTypes.string,
2424
disableUpDownKeys: PropTypes.bool,
2525
domRef: PropTypes.func,
26+
focusTabOnClick: PropTypes.bool,
2627
forceRenderTabPanel: PropTypes.bool,
2728
onSelect: onSelectPropType,
2829
selectedIndex: selectedIndexPropType,
@@ -32,6 +33,7 @@ const propTypes = {
3233
};
3334
const defaultProps = {
3435
defaultFocus: false,
36+
focusTabOnClick: true,
3537
forceRenderTabPanel: false,
3638
selectedIndex: null,
3739
defaultIndex: null,
@@ -64,7 +66,8 @@ For more information about controlled and uncontrolled mode of react-tabs see ht
6466
* It is initialized from the prop defaultFocus, and after the first render it is reset back to false. Later it can become true again when using keys to navigate the tabs.
6567
*/
6668
const Tabs = (props) => {
67-
const { children, defaultFocus, defaultIndex, onSelect } = props;
69+
const { children, defaultFocus, defaultIndex, focusTabOnClick, onSelect } =
70+
props;
6871

6972
const [focus, setFocus] = useState(defaultFocus);
7073
const [mode] = useState(getModeFromProps(props));
@@ -97,8 +100,10 @@ const Tabs = (props) => {
97100
if (onSelect(index, last, event) === false) return;
98101
}
99102

100-
// Always set focus on tabs
101-
setFocus(true);
103+
// Always set focus on tabs unless it is disabled
104+
if (focusTabOnClick) {
105+
setFocus(true);
106+
}
102107

103108
if (mode === MODE_UNCONTROLLED) {
104109
// Update selected index
@@ -116,6 +121,7 @@ const Tabs = (props) => {
116121
}
117122
delete subProps.defaultFocus;
118123
delete subProps.defaultIndex;
124+
delete subProps.focusTabOnClick;
119125
return <UncontrolledTabs {...subProps}>{children}</UncontrolledTabs>;
120126
};
121127

src/components/__tests__/Tabs-test.js

+15
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,21 @@ describe('<Tabs />', () => {
594594
assertTabSelected(1);
595595
});
596596

597+
test('should not focus tabs if focusTabOnClick is false', () => {
598+
render(createTabs({ focusTabOnClick: false }));
599+
const firstTab = screen.getByTestId('tab1');
600+
const secondTab = screen.getByTestId('tab2');
601+
602+
expect(firstTab).not.toHaveFocus();
603+
expect(secondTab).not.toHaveFocus();
604+
assertTabSelected(1);
605+
606+
userEvent.click(secondTab);
607+
expect(firstTab).not.toHaveFocus();
608+
expect(secondTab).not.toHaveFocus();
609+
assertTabSelected(2);
610+
});
611+
597612
test('should not change tabs when arrow up/down is pressed and disableUpDownKeys is passed', () => {
598613
render(
599614
createTabs({

0 commit comments

Comments
 (0)