Skip to content

Commit

Permalink
feat: Use new useId hook from react 18
Browse files Browse the repository at this point in the history
BREAKING CHANGE: React version 18 or newer is now required.
BREAKING CHANGE: `resetIdCounter` was removed as it is not necessary anymore. Ensure you remove any calls to it from your code.
  • Loading branch information
danez committed Apr 17, 2022
1 parent a799fb7 commit 487326c
Show file tree
Hide file tree
Showing 16 changed files with 240 additions and 427 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"react-component"
],
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0-0 || ^18.0.0"
"react": "^18.0.0"
},
"devDependencies": {
"@babel/cli": "7.17.6",
Expand Down
11 changes: 4 additions & 7 deletions src/components/Tab.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ import React, { useEffect, useRef } from 'react';
import cx from 'clsx';

const DEFAULT_CLASS = 'react-tabs__tab';
const DEFAULT_PROPS = {
const defaultProps = {
className: DEFAULT_CLASS,
disabledClassName: `${DEFAULT_CLASS}--disabled`,
focus: false,
id: null,
panelId: null,
selected: false,
selectedClassName: `${DEFAULT_CLASS}--selected`,
};
Expand All @@ -29,7 +28,6 @@ const propTypes = {
disabledClassName: PropTypes.string,
focus: PropTypes.bool, // private
id: PropTypes.string, // private
panelId: PropTypes.string, // private
selected: PropTypes.bool, // private
selectedClassName: PropTypes.string,
tabRef: PropTypes.func,
Expand All @@ -44,7 +42,6 @@ const Tab = (props) => {
disabledClassName,
focus,
id,
panelId,
selected,
selectedClassName,
tabIndex,
Expand All @@ -70,10 +67,10 @@ const Tab = (props) => {
if (tabRef) tabRef(node);
}}
role="tab"
id={id}
id={`tab${id}`}
aria-selected={selected ? 'true' : 'false'}
aria-disabled={disabled ? 'true' : 'false'}
aria-controls={panelId}
aria-controls={`panel${id}`}
tabIndex={tabIndex || (selected ? '0' : null)}
data-rttab
>
Expand All @@ -84,5 +81,5 @@ const Tab = (props) => {
Tab.propTypes = propTypes;

Tab.tabsRole = 'Tab';
Tab.defaultProps = DEFAULT_PROPS;
Tab.defaultProps = defaultProps;
export default Tab;
6 changes: 2 additions & 4 deletions src/components/TabPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ const propTypes = {
id: PropTypes.string, // private
selected: PropTypes.bool, // private
selectedClassName: PropTypes.string,
tabId: PropTypes.string, // private
};
const TabPanel = (props) => {
const {
Expand All @@ -29,7 +28,6 @@ const TabPanel = (props) => {
id,
selected,
selectedClassName,
tabId,
...attributes
} = props;

Expand All @@ -40,8 +38,8 @@ const TabPanel = (props) => {
[selectedClassName]: selected,
})}
role="tabpanel"
id={id}
aria-labelledby={tabId}
id={`panel${id}`}
aria-labelledby={`tab${id}`}
>
{forceRender || selected ? children : null}
</div>
Expand Down
13 changes: 4 additions & 9 deletions src/components/UncontrolledTabs.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import PropTypes from 'prop-types';
import React, { cloneElement, useRef } from 'react';
import React, { cloneElement, useRef, useId } from 'react';
import cx from 'clsx';
import uuid from '../helpers/uuid';
import { childrenPropType } from '../helpers/propTypes';
import { getTabsCount as getTabsCountHelper } from '../helpers/count';
import { deepMap } from '../helpers/childrenDeepMap';
Expand Down Expand Up @@ -71,7 +70,6 @@ const propTypes = {
const UncontrolledTabs = (props) => {
let tabNodes = useRef([]);
let tabIds = useRef([]);
let panelIds = useRef([]);
const ref = useRef();

function setSelected(index, event) {
Expand Down Expand Up @@ -180,15 +178,14 @@ const UncontrolledTabs = (props) => {
} = props;

tabIds.current = tabIds.current || [];
panelIds.current = panelIds.current || [];
let diff = tabIds.current.length - getTabsCount();

// Add ids if new tabs have been added
// Don't bother removing ids, just keep them in case they are added again
// This is more efficient, and keeps the uuid counter under control
const id = useId();
while (diff++ < 0) {
tabIds.current.push(uuid());
panelIds.current.push(uuid());
tabIds.current.push(`${id}${tabIds.current.length}`);
}

// Map children to dynamically setup refs
Expand Down Expand Up @@ -225,7 +222,6 @@ const UncontrolledTabs = (props) => {
tabNodes.current[key] = node;
},
id: tabIds.current[listIndex],
panelId: panelIds.current[listIndex],
selected,
focus: selected && (focus || wasTabFocused),
};
Expand All @@ -242,8 +238,7 @@ const UncontrolledTabs = (props) => {
});
} else if (isTabPanel(child)) {
const props = {
id: panelIds.current[index],
tabId: tabIds.current[index],
id: tabIds.current[index],
selected: selectedIndex === index,
};

Expand Down
2 changes: 1 addition & 1 deletion src/components/__tests__/Tab-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('<Tab />', () => {

it('should support being selected', () => {
expectToMatchSnapshot(
<Tab selected id="abcd" panelId="1234">
<Tab selected id="abcd">
Hello
</Tab>,
);
Expand Down
9 changes: 9 additions & 0 deletions src/components/__tests__/TabList-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ import TabPanel from '../TabPanel';
import Tabs from '../Tabs';
import { TabListWrapper, TabWrapper } from './helpers/higherOrder';

jest.mock('react', () => {
const originalModule = jest.requireActual('react');

return {
...originalModule,
useId: () => ':r0:',
};
});

function expectToMatchSnapshot(component) {
expect(renderer.create(component).toJSON()).toMatchSnapshot();
}
Expand Down
4 changes: 2 additions & 2 deletions src/components/__tests__/TabPanel-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,15 @@ describe('<TabPanel />', () => {

it('should support being selected', () => {
expectToMatchSnapshot(
<TabPanel selected id="abcd" tabId="1234">
<TabPanel selected id="abcd">
Hola
</TabPanel>,
);
});

it('should support being selected with custom class name', () => {
expectToMatchSnapshot(
<TabPanel selected id="abcd" tabId="1234" selectedClassName="selected">
<TabPanel selected id="abcd" selectedClassName="selected">
Hola
</TabPanel>,
);
Expand Down
11 changes: 9 additions & 2 deletions src/components/__tests__/Tabs-errors-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,15 @@ import Tab from '../Tab';
import TabList from '../TabList';
import TabPanel from '../TabPanel';
import Tabs from '../Tabs';
import { reset as resetIdCounter } from '../../helpers/uuid';

jest.mock('react', () => {
const originalModule = jest.requireActual('react');

return {
...originalModule,
useId: () => ':r0:',
};
});

describe('<Tabs />', () => {
let consoleErrorMock;
Expand All @@ -21,7 +29,6 @@ describe('<Tabs />', () => {
}

beforeEach(() => {
resetIdCounter();
consoleErrorMock = jest.spyOn(console, 'error').mockImplementation();
});

Expand Down
19 changes: 9 additions & 10 deletions src/components/__tests__/Tabs-node-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,15 @@ import Tab from '../Tab';
import TabList from '../TabList';
import TabPanel from '../TabPanel';
import Tabs from '../Tabs';
import { reset as resetIdCounter } from '../../helpers/uuid';

jest.mock('react', () => {
const originalModule = jest.requireActual('react');

return {
...originalModule,
useId: () => ':r0:',
};
});

function createTabs(props = {}) {
return (
Expand All @@ -28,15 +36,6 @@ function createTabs(props = {}) {
}

describe('ServerSide <Tabs />', () => {
beforeEach(() => resetIdCounter());

beforeAll(() => {
// eslint-disable-next-line no-console
console.error = (error) => {
throw new Error(error);
};
});

test('does not crash in node environments', () => {
expect(() => createTabs()).not.toThrow();
});
Expand Down
22 changes: 9 additions & 13 deletions src/components/__tests__/Tabs-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,21 @@ import Tab from '../Tab';
import TabList from '../TabList';
import TabPanel from '../TabPanel';
import Tabs from '../Tabs';
import { reset as resetIdCounter } from '../../helpers/uuid';
import {
TabListWrapper,
TabWrapper,
TabPanelWrapper,
} from './helpers/higherOrder';

jest.mock('react', () => {
const originalModule = jest.requireActual('react');

return {
...originalModule,
useId: () => ':r0:',
};
});

function expectToMatchSnapshot(component) {
expect(render(component).container.firstChild).toMatchSnapshot();
}
Expand Down Expand Up @@ -47,8 +55,6 @@ function assertTabSelected(tabNo, node = screen) {
}

describe('<Tabs />', () => {
beforeEach(() => resetIdCounter());

describe('props', () => {
test('should have sane defaults', () => {
expectToMatchSnapshot(createTabs());
Expand Down Expand Up @@ -98,16 +104,6 @@ describe('<Tabs />', () => {
});
});

describe('child props', () => {
test('should reset ids correctly', () => {
expectToMatchSnapshot(createTabs());

resetIdCounter();

expectToMatchSnapshot(createTabs());
});
});

describe('interaction', () => {
describe('mouse', () => {
test('should update selectedIndex when clicked', async () => {
Expand Down
Loading

0 comments on commit 487326c

Please sign in to comment.