Skip to content

Commit

Permalink
feat: New prop to disable focusing tabs on click
Browse files Browse the repository at this point in the history
  • Loading branch information
danez committed Mar 31, 2022
1 parent f849c30 commit 85caf7a
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 18 deletions.
43 changes: 28 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ https://reactcommunity.org/react-tabs/
> Supports React 16.8.0 or newer
<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>
<img src="./examples/src/images/Browserstack-logo.svg" height="50" alt="Browserstack">
<img src="./examples/src/images/Browserstack-logo.svg" height="50" alt="Browserstack">

</p> </blockquote></ins>

Expand All @@ -16,7 +16,9 @@ https://reactcommunity.org/react-tabs/
```bash
yarn add react-tabs
```

or

```bash
npm install --save react-tabs
```
Expand Down Expand Up @@ -78,7 +80,7 @@ react-tabs consists of 4 components which all need to be used together.
If you specify additional props on the `<Tabs />` component they will be forwarded to the rendered `<div />`.
#### className: `string | Array<string> | { [string]: boolean }`
#### className: `string | Array<string> | { [string]: boolean }`
> default: `"react-tabs"`
Expand Down Expand Up @@ -130,6 +132,14 @@ Register a callback that will receive the underlying DOM node for every mount. I
If you're rendering `react-tabs` within a different `window` context than the default one; for example, an iframe.
#### focusTabOnClick: `boolean`
> default: `true`
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.
> 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.
#### forceRenderTabPanel: `boolean`
> default: `false`
Expand Down Expand Up @@ -178,7 +188,7 @@ Provide a custom class name for the active tab panel.
If you specify additional props on the `<TabList />` component they will be forwarded to the rendered `<ul />`.
#### className: `string | Array<string> | { [string]: boolean }`
#### className: `string | Array<string> | { [string]: boolean }`
> default: `"react-tabs__tab-list"`
Expand All @@ -190,7 +200,7 @@ Provide a custom class name for the `<ul />`.
If you specify additional props on the `<Tab />` component they will be forwarded to the rendered `<li />`.
#### className: `string | Array<string> | { [string]: boolean }`
#### className: `string | Array<string> | { [string]: boolean }`
> default: `"react-tabs__tab"`
Expand Down Expand Up @@ -230,7 +240,7 @@ Overrides the tabIndex to enabled tabbing between tabs.
If you specify additional props on the `<TabPanel />` component they will be forwarded to the rendered `<div />`.
#### className: `string | Array<string> | { [string]: boolean }`
#### className: `string | Array<string> | { [string]: boolean }`
> default: `"react-tabs__tab-panel"`
Expand Down Expand Up @@ -265,7 +275,7 @@ This is the default mode of react-tabs and makes the react-tabs components handl
In this mode you cannot force a tab change during runtime.
```js
<Tabs defaultIndex={1} onSelect={index => console.log(index)}>
<Tabs defaultIndex={1} onSelect={(index) => console.log(index)}>
<TabList>
<Tab>Title 1</Tab>
<Tab>Title 2</Tab>
Expand All @@ -288,7 +298,7 @@ const App = () => {
const [tabIndex, setTabIndex] = useState(0);
return (
<Tabs selectedIndex={tabIndex} onSelect={index => setTabIndex(index)}>
<Tabs selectedIndex={tabIndex} onSelect={(index) => setTabIndex(index)}>
<TabList>
<Tab>Title 1</Tab>
<Tab>Title 2</Tab>
Expand Down Expand Up @@ -340,33 +350,36 @@ When using the UMD version of react-tabs you can easily use the default styles b
<html>
<head>
...
<link rel="stylesheet" href="https://unpkg.com/react-tabs/style/react-tabs.css">
<link
rel="stylesheet"
href="https://unpkg.com/react-tabs/style/react-tabs.css"
/>
</head>
...
</html>
```
### Custom Style
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.
### Custom Components
#### Set `tabsRole`
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 />`.
Possible values for tabsRole are:
* Tab
* TabPanel
* TabList
#### Pass through properties
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.
- Tab
- TabPanel
- TabList
#### Pass through properties
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.
``` javascript
```javascript
import { Tabs, TabList, Tab, TabPanel } from 'react-tabs';
// All custom elements should pass through other props
Expand Down
12 changes: 9 additions & 3 deletions src/components/Tabs.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const propTypes = {
disabledTabClassName: PropTypes.string,
disableUpDownKeys: PropTypes.bool,
domRef: PropTypes.func,
focusTabOnClick: PropTypes.bool,
forceRenderTabPanel: PropTypes.bool,
onSelect: onSelectPropType,
selectedIndex: selectedIndexPropType,
Expand All @@ -32,6 +33,7 @@ const propTypes = {
};
const defaultProps = {
defaultFocus: false,
focusTabOnClick: true,
forceRenderTabPanel: false,
selectedIndex: null,
defaultIndex: null,
Expand Down Expand Up @@ -64,7 +66,8 @@ For more information about controlled and uncontrolled mode of react-tabs see ht
* 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.
*/
const Tabs = (props) => {
const { children, defaultFocus, defaultIndex, onSelect } = props;
const { children, defaultFocus, defaultIndex, focusTabOnClick, onSelect } =
props;

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

// Always set focus on tabs
setFocus(true);
// Always set focus on tabs unless it is disabled
if (focusTabOnClick) {
setFocus(true);
}

if (mode === MODE_UNCONTROLLED) {
// Update selected index
Expand All @@ -116,6 +121,7 @@ const Tabs = (props) => {
}
delete subProps.defaultFocus;
delete subProps.defaultIndex;
delete subProps.focusTabOnClick;
return <UncontrolledTabs {...subProps}>{children}</UncontrolledTabs>;
};

Expand Down
15 changes: 15 additions & 0 deletions src/components/__tests__/Tabs-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,21 @@ describe('<Tabs />', () => {
assertTabSelected(1);
});

test('should not focus tabs if focusTabOnClick is false', () => {
render(createTabs({ focusTabOnClick: false }));
const firstTab = screen.getByTestId('tab1');
const secondTab = screen.getByTestId('tab2');

expect(firstTab).not.toHaveFocus();
expect(secondTab).not.toHaveFocus();
assertTabSelected(1);

userEvent.click(secondTab);
expect(firstTab).not.toHaveFocus();
expect(secondTab).not.toHaveFocus();
assertTabSelected(2);
});

test('should not change tabs when arrow up/down is pressed and disableUpDownKeys is passed', () => {
render(
createTabs({
Expand Down

0 comments on commit 85caf7a

Please sign in to comment.