Skip to content

Commit 5d96a78

Browse files
authored
feat: add React 19 support with backward compatibility for React 18.3+ (#66)
- Update peerDependencies to support React 18.3.0 || 19.0.0 - Upgrade devDependencies to React 19.2.4 and @types/react 19.2.14 - Add global JSX namespace declaration for React 19 compatibility - Fix 30+ files with RefObject<T | null> types for React 19 stricter typing - Add explicit initial values to useRef() calls (React 19 requirement) - Fix 6 test errors related to ref callbacks and mock RefObjects - Add pnpm overrides for eslint-plugin-react-hooks canary version (ESLint 10 compatibility) - Add zod-validation-error override to resolve ESLint 10 package exports issue Affected packages: - @kubit-ui-web/react-components - @kubit-ui-web/design-system (types only) - @kubit-ui-web/storybook TypeScript errors: 56 → 0 ✅ The library maintains full compatibility with React 18.3+ while supporting React 19. No React 19 exclusive features are used to preserve backward compatibility.
1 parent 23fe93b commit 5d96a78

40 files changed

Lines changed: 284 additions & 221 deletions

package.json

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,13 @@
2525
"engines": {
2626
"node": "22.x"
2727
},
28-
"packageManager": "pnpm@10.28.1",
28+
"packageManager": "pnpm@10.29.3",
29+
"pnpm": {
30+
"overrides": {
31+
"eslint-plugin-react-hooks": "7.1.0-canary-272441a9-20260209",
32+
"zod-validation-error": "^3.4.0"
33+
}
34+
},
2935
"scripts": {
3036
"dev": "turbo run dev --filter=@kubit-ui-web/storybook",
3137
"dev:components": "turbo run dev --filter=@kubit-ui-web/react-components",
@@ -78,7 +84,7 @@
7884
},
7985
"devDependencies": {
8086
"@changesets/cli": "^2.29.8",
81-
"turbo": "^2.8.1"
87+
"turbo": "^2.8.7"
8288
},
8389
"bugs": {
8490
"url": "https://github.com/kubit-ui/kubit-react-components/issues",

packages/components/package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,8 @@
166166
"prerelease": "pnpm validate && pnpm test"
167167
},
168168
"peerDependencies": {
169-
"react": ">=17.0.0 || >=18.3.1",
170-
"react-dom": ">=17.0.0 || >=18.3.1"
169+
"react": "^18.3.0 || ^19.0.0",
170+
"react-dom": "^18.3.0 || ^19.0.0"
171171
},
172172
"dependencies": {
173173
"@floating-ui/dom": "^1.7.5"
@@ -180,8 +180,8 @@
180180
"@testing-library/user-event": "^14.6.1",
181181
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
182182
"@types/node": "^25.2.3",
183-
"@types/react": "^18.3.12",
184-
"@types/react-dom": "^18.3.1",
183+
"@types/react": "^19.2.14",
184+
"@types/react-dom": "^19.2.3",
185185
"@types/testing-library__jest-dom": "^6.0.0",
186186
"@vitejs/plugin-react": "^5.1.4",
187187
"@vitest/coverage-v8": "^4.0.18",
@@ -193,8 +193,8 @@
193193
"html-validate": "^10.7.0",
194194
"jsdom": "^28.0.0",
195195
"prettier": "^3.8.1",
196-
"react": "18.3.1",
197-
"react-dom": "18.3.1",
196+
"react": "^19.2.4",
197+
"react-dom": "^19.2.4",
198198
"typescript": "^5.9.3",
199199
"typescript-eslint": "^8.55.0",
200200
"vite": "8.0.0-beta.10",

packages/components/src/components/accordion/hooks/useAccordionContentOverflow.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { type RefObject, useEffect, useRef } from 'react';
22

33
interface UseAccordionContentOverflowParamsType {
4-
ref: RefObject<HTMLDivElement>;
4+
ref: RefObject<HTMLDivElement | null>;
55
expanded: boolean;
66
}
77

packages/components/src/components/accordion/hooks/useAccordionInertContent.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { type RefObject, useEffect } from 'react';
22

33
interface UseAccordionInertContentParamsType {
4-
ref: RefObject<HTMLDivElement>;
4+
ref: RefObject<HTMLDivElement | null>;
55
expanded: boolean;
66
}
77

packages/components/src/components/carousel/__tests__/carousel.test.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { act } from 'react';
2+
23
import { axe } from 'vitest-axe';
34

45
import { render } from '@/lib/tests/render/render';
56

6-
import type { ICarousel } from '../types/carousel';
7-
87
import { Carousel } from '../carousel';
98
import { useCarousel } from '../hooks/useCarousel';
9+
import type { ICarousel } from '../types/carousel';
1010

1111
const mockProps: ICarousel = {
1212
elements: [
@@ -28,7 +28,7 @@ const mockUseCarousel = vi.mocked(useCarousel);
2828

2929
describe('Carousel component', () => {
3030
it('Should render carousel component', async () => {
31-
const { container } = render(<Carousel {...mockProps} ref={() => ({})} />);
31+
const { container } = render(<Carousel {...mockProps} ref={() => {}} />);
3232

3333
const results = await axe(container);
3434
expect(container).toHTMLValidate({
@@ -43,7 +43,7 @@ describe('Carousel component', () => {
4343
render(
4444
<Carousel
4545
{...mockProps}
46-
ref={() => ({})}
46+
ref={() => {}}
4747
numElementsPerPage={mockProps.elements.length + 2}
4848
/>,
4949
);
@@ -59,7 +59,7 @@ describe('Carousel component', () => {
5959
render(
6060
<Carousel
6161
{...mockProps}
62-
ref={() => ({})}
62+
ref={() => {}}
6363
allowModifySliceWidth={true}
6464
numElementsPerPage={0}
6565
/>,
@@ -76,7 +76,7 @@ describe('Carousel component', () => {
7676
render(
7777
<Carousel
7878
{...mockProps}
79-
ref={() => ({})}
79+
ref={() => {}}
8080
allowModifySliceWidth={true}
8181
autoFitContainer={true}
8282
numElementsPerPage={5}

packages/components/src/components/carousel/hooks/types/useCarousel.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import type {
44
} from '../../types/carousel';
55

66
export interface IUseCarouselParams {
7-
rootContainerRef: React.RefObject<HTMLDivElement>;
8-
viewerContainerRef: React.RefObject<HTMLDivElement>;
9-
contentContainerRef: React.RefObject<HTMLDivElement>;
7+
rootContainerRef: React.RefObject<HTMLDivElement | null>;
8+
viewerContainerRef: React.RefObject<HTMLDivElement | null>;
9+
contentContainerRef: React.RefObject<HTMLDivElement | null>;
1010
circular?: boolean;
1111
centerMode?: boolean;
1212
extraPadding?: number;

packages/components/src/components/carousel/hooks/types/useCarouselKeyNavigation.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export interface IUseCarouselKeyNavigationParams {
2-
rootContainerRef: React.RefObject<HTMLDivElement>;
2+
rootContainerRef: React.RefObject<HTMLDivElement | null>;
33
allowShiftRef: React.MutableRefObject<boolean>;
44
circular: boolean;
55
numPagesRef: React.MutableRefObject<number>;

packages/components/src/components/carousel/hooks/types/useCarouselSwipe.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export interface IUseCarouselSwipeParams {
2-
viewerContainerRef: React.RefObject<HTMLDivElement>;
3-
contentContainerRef: React.RefObject<HTMLDivElement>;
2+
viewerContainerRef: React.RefObject<HTMLDivElement | null>;
3+
contentContainerRef: React.RefObject<HTMLDivElement | null>;
44
allowShiftRef: React.MutableRefObject<boolean>;
55
circular: boolean;
66
extraPadding: number;

packages/components/src/components/carousel/hooks/useCarouselSwipe.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { useCallback, useEffect, useRef } from 'react';
33
import { useScrollBlock } from '@/lib/hooks/useScrollBlock/useScrollBlock';
44

55
import type { IUseCarouselSwipe } from './types/useCarouselSwipe';
6-
76
import calcUtils from './utils/calc.utils';
87
import CONSTANTS from './utils/constants';
98

@@ -25,7 +24,7 @@ export const useCarouselSwipe: IUseCarouselSwipe = ({
2524
const isDragging = useRef(false);
2625
const isHorizontalDragging = useRef(false);
2726
const isVerticalDragging = useRef(false);
28-
const posInitial = useRef<number | undefined>();
27+
const posInitial = useRef<number | undefined>(undefined);
2928
const posX1 = useRef(0);
3029
const posX2 = useRef(0);
3130
const posXInitial = useRef(0);

packages/components/src/components/carousel/types/carousel.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ export type CarouselScreenReaderOnlyType = Omit<
2323

2424
export interface ICarouselStandAlone extends DataAttributes {
2525
cssClasses?: CarouselCssClasses;
26-
viewerContainerRef: React.RefObject<HTMLDivElement>;
27-
contentContainerRef: React.RefObject<HTMLDivElement>;
26+
viewerContainerRef: React.RefObject<HTMLDivElement | null>;
27+
contentContainerRef: React.RefObject<HTMLDivElement | null>;
2828
elements: JSX.Element[];
2929
screenReaderOnly?: CarouselScreenReaderOnlyType;
3030
allowModifySliceWidth?: boolean;

0 commit comments

Comments
 (0)