Skip to content

Commit 5cde977

Browse files
committed
Merge branch '2026-05-typescript-conversion' into master
Convert the library from Flow to TypeScript and prep the 4.0.0 release. Public runtime API is unchanged. Type-delivery channel changes: - Bundled .d.ts now ships in build/ (via package.json#types). - *.js.flow shims removed. Flow is no longer supported. - @types/react-resizable on DefinitelyTyped is no longer needed. See CHANGELOG.md (4.0.0) for migration notes and the link to the last Flow-annotated source for users who still need to vendor Flow types.
2 parents db2e37e + ab0bc4f commit 5cde977

29 files changed

Lines changed: 1055 additions & 1476 deletions

.babelrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
}
88
],
99
"@babel/preset-react",
10-
"@babel/preset-flow"
10+
"@babel/preset-typescript"
1111
],
1212
"plugins": [
1313
["@babel/plugin-proposal-class-properties", {"loose": true}],

.claude/commands/goal.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
---
2+
description: Convert react-resizable from Flow to TypeScript
3+
---
4+
5+
# Goal: Convert react-resizable from Flow to TypeScript
6+
7+
## Why
8+
9+
The repo is currently typed with Flow (v0.153.0, pinned to a 2020 version). Flow is no longer well-supported in the React ecosystem and most consumers expect TypeScript declarations. Convert the library source, tests, and build pipeline to TypeScript while preserving all public runtime behavior (including PropTypes) and the existing build output shape (CommonJS lib + ambient declarations consumed via `build/`).
10+
11+
## Constraints
12+
13+
- No behavior changes. Snapshots and unit tests must continue to pass.
14+
- The public API surface is unchanged. `require('react-resizable').Resizable` and `.ResizableBox` must keep working.
15+
- Keep `prop-types` runtime validation in place (downstream JS consumers rely on it).
16+
- Continue to emit a CommonJS bundle from `build/` plus type declarations next to each module.
17+
- Drop Flow (`flow-bin`, `.flowconfig`, `flow-typed/`, `@babel/preset-flow`, `// @flow` pragmas).
18+
19+
## Acceptance criteria
20+
21+
- [ ] `yarn install` succeeds.
22+
- [ ] `yarn typecheck` (tsc --noEmit) passes with no errors.
23+
- [ ] `yarn lint` passes (ESLint with @typescript-eslint).
24+
- [ ] `yarn test` passes (all existing tests, including snapshots).
25+
- [ ] `yarn build` produces `build/Resizable.js`, `build/ResizableBox.js`, `build/utils.js`, `build/propTypes.js` plus matching `.d.ts` declaration files.
26+
- [ ] `index.js` still resolves to the built output.
27+
- [ ] `package.json` advertises `"types": "./build/index.d.ts"` (or equivalent) so TS consumers get IntelliSense.
28+
- [ ] No `.flow` files remain in `lib/`. No `// @flow` pragmas.
29+
30+
## Plan
31+
32+
1. **Tooling**: add `typescript`, `@types/react`, `@types/react-dom`, `@types/prop-types`, `@types/jest`, `@babel/preset-typescript`, `@typescript-eslint/parser`, `@typescript-eslint/eslint-plugin`. Remove `flow-bin`, `@babel/preset-flow`. Write `tsconfig.json` (target ES2020, jsx react, declaration true, declarationDir ./build, noEmit configurable via separate `tsconfig.build.json`).
33+
2. **Babel**: replace `@babel/preset-flow` with `@babel/preset-typescript` in `.babelrc`.
34+
3. **Source conversion (`lib/`)**:
35+
- `utils.js``utils.ts`
36+
- `propTypes.js``propTypes.ts` (keep runtime PropTypes; export TS types alongside)
37+
- `Resizable.js``Resizable.tsx`
38+
- `ResizableBox.js``ResizableBox.tsx`
39+
- Map Flow types: `?T``T | null | undefined`, `SyntheticEvent<>``React.SyntheticEvent`, `Node as ReactNode``React.ReactNode`, `Element as ReactElement``React.ReactElement`, `ElementConfig<typeof X>``React.ComponentProps<typeof X>`, `ReactRef<T>``React.RefObject<T>`.
40+
- Strip `// @flow` pragmas.
41+
4. **Tests (`__tests__/`)**:
42+
- Rename `.test.js``.test.tsx` where JSX is used, otherwise `.test.ts`.
43+
- Fix any test typings that need help (e.g., enzyme/testing-library queries).
44+
- Re-record snapshots only if structural output is unchanged.
45+
5. **Build pipeline**:
46+
- `build.sh` runs babel (transpile .ts/.tsx → JS) and `tsc --emitDeclarationOnly` for .d.ts files.
47+
- Drop the `cp *.js *.js.flow` step.
48+
6. **package.json scripts**:
49+
- `lint`: ESLint over `.ts,.tsx`.
50+
- `typecheck`: `tsc --noEmit`.
51+
- Remove the `flow` script.
52+
7. **ESLint**: update `eslint.config.js` to parse TS with `@typescript-eslint/parser` and include `@typescript-eslint` rules at "recommended" level. Keep existing react/jest plugins.
53+
8. **Examples**: keep `examples/example.js` as JS — out of scope for this conversion. Update webpack to still build it.
54+
9. **Run the acceptance gate**: `yarn install && yarn typecheck && yarn lint && yarn test && yarn build`. Fix everything until green.
55+
10. **Commit**: one commit, message `chore: convert library from Flow to TypeScript`.
56+
57+
## Out of scope
58+
59+
- Examples directory (`examples/*.js`).
60+
- API/behavior changes.
61+
- Bumping major dependency versions beyond what's needed for TS to work.
62+
- Migrating ESLint plugins beyond what's needed for TS parsing.

.flowconfig

Lines changed: 0 additions & 25 deletions
This file was deleted.

.github/workflows/test.yml

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,28 @@ jobs:
1313
runs-on: ubuntu-latest
1414
steps:
1515
- name: Check out Git repository
16-
uses: actions/checkout@v2
16+
uses: actions/checkout@v4
1717

1818
- name: Set up Node.js
19-
uses: actions/setup-node@v2
19+
uses: actions/setup-node@v4
2020
with:
2121
node-version: 24
22+
cache: yarn
2223

2324
- name: Install Node.js dependencies
24-
run: yarn
25+
run: yarn install --frozen-lockfile
26+
27+
- name: Lint
28+
run: yarn lint
29+
30+
- name: Type-check
31+
run: yarn typecheck
2532

2633
- name: Run tests
2734
run: yarn test
35+
36+
- name: Build (verify .d.ts emission)
37+
run: yarn build
38+
39+
- name: Smoke-test built artifact
40+
run: node ./__tests__/integration/smoke.cjs

CHANGELOG.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,60 @@
11
# Changelog
22

3+
### 4.0.0 (May 12, 2026)
4+
5+
This release converts the library from Flow to TypeScript and ships bundled
6+
type declarations. The runtime API and on-screen behavior are unchanged. The
7+
major version bump is to flag two things consumers may need to act on.
8+
9+
**Breaking — types delivery**
10+
11+
- The library is now authored in TypeScript. Bundled type declarations ship
12+
in `build/*.d.ts` and are resolved via the `"types"` field in
13+
`package.json`. No `@types/react-resizable` install is needed; if you have
14+
one installed, remove it — TypeScript prefers the bundled types and the
15+
DefinitelyTyped package is no longer required.
16+
- The `*.js.flow` shims previously emitted by `build.sh` have been removed.
17+
Flow is no longer supported.
18+
19+
Flow users who still need types can copy the last Flow-annotated source
20+
from the `3.2.0` tag:
21+
[`react-grid-layout/react-resizable@db2e37e/lib`](https://github.com/react-grid-layout/react-resizable/tree/db2e37eda85fb21b1864e36b01c4922452f28418/lib)
22+
([`lib/Resizable.js`](https://github.com/react-grid-layout/react-resizable/blob/db2e37eda85fb21b1864e36b01c4922452f28418/lib/Resizable.js),
23+
[`lib/ResizableBox.js`](https://github.com/react-grid-layout/react-resizable/blob/db2e37eda85fb21b1864e36b01c4922452f28418/lib/ResizableBox.js),
24+
[`lib/propTypes.js`](https://github.com/react-grid-layout/react-resizable/blob/db2e37eda85fb21b1864e36b01c4922452f28418/lib/propTypes.js),
25+
[`lib/utils.js`](https://github.com/react-grid-layout/react-resizable/blob/db2e37eda85fb21b1864e36b01c4922452f28418/lib/utils.js)).
26+
These files were the input to the published `.js.flow` artifacts in
27+
`react-resizable@3.2.0` on npm; you can vendor them as a local Flow shim.
28+
They will not be updated, and any fix landing on `master` after `4.0.0`
29+
will not be backported into a Flow form.
30+
31+
**Internal**
32+
33+
- ✏ Chore: Migrate `lib/` and `__tests__/` from Flow to TypeScript
34+
(`*.ts`/`*.tsx`). Public runtime API unchanged.
35+
- ✏ Chore: Replace `@babel/preset-flow` with `@babel/preset-typescript`;
36+
drop `flow-bin`, `flow-typed/`, `.flowconfig`.
37+
- ✏ Chore: Add `tsconfig.json` (typecheck), `tsconfig.build.json`
38+
(declaration emit), `tsconfig.test.json`.
39+
- ✏ Chore: `build.sh` now runs `babel` for the JS transform and
40+
`tsc --emitDeclarationOnly` for the `.d.ts` files.
41+
- ✏ Chore: ESLint now uses `@typescript-eslint/parser`.
42+
- ✏ Chore: CI now runs `yarn lint`, `yarn typecheck`, `yarn test`, and a
43+
build-artifact smoke test on every push and PR.
44+
- ✏ Chore: Add an integration smoke test that imports the built CJS entry
45+
point and asserts the public surface (`Resizable`, `ResizableBox`).
46+
47+
### 3.2.0 (May 11, 2026)
48+
49+
- 🐛 Bugfix: Prevent resize drift caused by stale props between renders.
50+
The component now accumulates deltas from `lastSize` rather than
51+
`this.props.width/height`, and `dimensionsChanged` is compared against
52+
that same base so zero-delta callbacks are still suppressed correctly.
53+
[#255](https://github.com/react-grid-layout/react-resizable/pull/255)
54+
- ✏ Chore: Pin ESLint to `^9.x`. ESLint 10 removes `scopeManager.addGlobals`,
55+
which `@babel/eslint-parser` 7.x still calls; the parser fix is only
56+
available in unreleased v8 RCs that require an unreleased Babel core v8.
57+
358
### 3.1.3 (Jan 1, 2026)
459

560
- ✏ Chore: Add `files` whitelist to package.json to reduce package size and exclude unnecessary files.

README.md

Lines changed: 73 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@ A simple widget that can be resized via one or more handles.
1111
You can either use the `<Resizable>` element directly, or use the much simpler `<ResizableBox>` element.
1212

1313
See the example and associated code in [ExampleLayout](https://github.com/react-grid-layout/react-resizable/blob/master/examples/ExampleLayout.js) and
14-
[ResizableBox](https://github.com/react-grid-layout/react-resizable/blob/master/lib/ResizableBox.js) for more details.
14+
[ResizableBox](https://github.com/react-grid-layout/react-resizable/blob/master/lib/ResizableBox.tsx) for more details.
1515

1616
## Table of Contents
1717

1818
- [Installation](#installation)
19+
- [TypeScript](#typescript)
1920
- [Compatibility](#compatibility)
2021
- [Usage](#usage)
2122
- [Resizable](#resizable)
@@ -47,20 +48,59 @@ Or import it in your CSS:
4748

4849
If you're using a bundler that doesn't support CSS imports, you can find the styles at `node_modules/react-resizable/css/styles.css` and include them manually.
4950

51+
## TypeScript
52+
53+
As of `4.0.0`, the library is authored in TypeScript and ships bundled type
54+
declarations in `build/*.d.ts`. You **do not** need to install
55+
`@types/react-resizable`; if you previously installed it, remove it so the
56+
bundled types take precedence:
57+
58+
```bash
59+
npm uninstall @types/react-resizable
60+
# or
61+
yarn remove @types/react-resizable
62+
```
63+
64+
Public types are re-exported from the package root:
65+
66+
```ts
67+
import {
68+
Resizable,
69+
ResizableBox,
70+
// types
71+
type ResizeCallbackData,
72+
type ResizeHandleAxis,
73+
type Axis,
74+
type Props as ResizableProps,
75+
} from 'react-resizable';
76+
```
77+
78+
### Flow
79+
80+
Flow is no longer supported as of `4.0.0`. Earlier versions shipped
81+
`*.js.flow` sidecar files generated from the Flow-annotated source; those
82+
have been removed.
83+
84+
If you still need Flow types, you can vendor the last Flow-annotated source
85+
locally from the [`3.2.0` tag](https://github.com/react-grid-layout/react-resizable/tree/db2e37eda85fb21b1864e36b01c4922452f28418/lib).
86+
They will not be updated to reflect changes landing after `4.0.0`. The
87+
official recommendation is to migrate to TypeScript.
88+
5089
## Compatibility
5190

52-
| Version | React Version |
53-
|---------|---------------|
54-
| [3.x](https://github.com/react-grid-layout/react-resizable/blob/master/CHANGELOG.md#300-may-10-2021) | `>= 16.3` |
55-
| 2.x | Skipped |
56-
| [1.x](https://github.com/react-grid-layout/react-resizable/blob/master/CHANGELOG.md#1111-mar-5-2021) | `14 - 17` |
91+
| Version | React Version | Types |
92+
|---------|---------------|----------------|
93+
| [4.x](https://github.com/react-grid-layout/react-resizable/blob/master/CHANGELOG.md#400-may-12-2026) | `>= 16.3` | TypeScript (bundled) |
94+
| [3.x](https://github.com/react-grid-layout/react-resizable/blob/master/CHANGELOG.md#300-may-10-2021) | `>= 16.3` | Flow (`*.js.flow`) |
95+
| 2.x | Skipped ||
96+
| [1.x](https://github.com/react-grid-layout/react-resizable/blob/master/CHANGELOG.md#1111-mar-5-2021) | `14 - 17` | Flow |
5797

5898
## Usage
5999

60100
This package has two major exports:
61101

62-
* [`<Resizable>`](https://github.com/react-grid-layout/react-resizable/blob/master/lib/Resizable.js): A raw component that does not have state. Use as a building block for larger components, by listening to its callbacks and setting its props.
63-
* [`<ResizableBox>`](https://github.com/react-grid-layout/react-resizable/blob/master/lib/ResizableBox.js): A simple `<div {...props} />` element that manages basic state. Convenient for simple use-cases.
102+
* [`<Resizable>`](https://github.com/react-grid-layout/react-resizable/blob/master/lib/Resizable.tsx): A raw component that does not have state. Use as a building block for larger components, by listening to its callbacks and setting its props.
103+
* [`<ResizableBox>`](https://github.com/react-grid-layout/react-resizable/blob/master/lib/ResizableBox.tsx): A simple `<div {...props} />` element that manages basic state. Convenient for simple use-cases.
64104

65105
### `<Resizable>`
66106

@@ -124,43 +164,46 @@ class Example extends React.Component {
124164

125165
These props apply to both `<Resizable>` and `<ResizableBox>`. Unknown props that are not in the list below will be passed to the child component.
126166

127-
```js
167+
```ts
128168
type ResizeCallbackData = {
129-
node: HTMLElement,
130-
size: {width: number, height: number},
131-
handle: ResizeHandleAxis
169+
node: HTMLElement;
170+
size: {width: number; height: number};
171+
handle: ResizeHandleAxis;
132172
};
133173

134174
type ResizeHandleAxis = 's' | 'w' | 'e' | 'n' | 'sw' | 'nw' | 'se' | 'ne';
135175

136176
type ResizableProps = {
137-
children: React.Element<any>,
138-
width: number,
139-
height: number,
177+
children: React.ReactElement<any>;
178+
width: number;
179+
height: number;
140180
// Either a ReactElement to be used as handle, or a function
141181
// returning an element that is fed the handle's location as its first argument.
142-
handle: ReactElement<any> | (resizeHandle: ResizeHandleAxis, ref: ReactRef<HTMLElement>) => ReactElement<any>,
143-
// If you change this, be sure to update your css
144-
handleSize: [number, number] = [10, 10],
145-
lockAspectRatio: boolean = false,
146-
axis: 'both' | 'x' | 'y' | 'none' = 'both',
147-
minConstraints: [number, number] = [10, 10],
148-
maxConstraints: [number, number] = [Infinity, Infinity],
149-
onResizeStop?: ?(e: SyntheticEvent, data: ResizeCallbackData) => any,
150-
onResizeStart?: ?(e: SyntheticEvent, data: ResizeCallbackData) => any,
151-
onResize?: ?(e: SyntheticEvent, data: ResizeCallbackData) => any,
152-
draggableOpts?: ?Object,
153-
resizeHandles?: ?Array<ResizeHandleAxis> = ['se'],
182+
handle?:
183+
| React.ReactElement<any>
184+
| ((resizeHandle: ResizeHandleAxis, ref: React.RefObject<HTMLElement>) => React.ReactElement<any>);
185+
// If you change this, be sure to update your css. Default: [20, 20].
186+
handleSize?: [number, number];
187+
lockAspectRatio?: boolean; // default: false
188+
axis?: 'both' | 'x' | 'y' | 'none'; // default: 'both'
189+
minConstraints?: [number, number]; // default: [20, 20]
190+
maxConstraints?: [number, number]; // default: [Infinity, Infinity]
191+
onResizeStop?: (e: React.SyntheticEvent, data: ResizeCallbackData) => any;
192+
onResizeStart?: (e: React.SyntheticEvent, data: ResizeCallbackData) => any;
193+
onResize?: (e: React.SyntheticEvent, data: ResizeCallbackData) => any;
194+
// Forwarded to react-draggable's <DraggableCore>.
195+
draggableOpts?: Partial<React.ComponentProps<typeof import('react-draggable').DraggableCore>>;
196+
resizeHandles?: ResizeHandleAxis[]; // default: ['se']
154197
// If `transform: scale(n)` is set on the parent, this should be set to `n`.
155-
transformScale?: number = 1
198+
transformScale?: number; // default: 1
156199
};
157200
```
158201

159202
The following props can also be used on `<ResizableBox>`:
160203

161-
```js
204+
```ts
162205
{
163-
style?: Object // styles the returned <div />
206+
style?: React.CSSProperties; // styles the returned <div />
164207
}
165208
```
166209

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ describe('render ResizableBox', () => {
88
const props = {
99
axis: 'x',
1010
draggableOpts: {},
11-
handle: (jest.fn((resizeHandle, ref) => <span className={`test-class-${resizeHandle}`} ref={ref} />): Function),
11+
handle: jest.fn((resizeHandle, ref) => <span className={`test-class-${resizeHandle}`} ref={ref} />) as any,
1212
handleSize: [20, 20],
1313
height: 50,
1414
lockAspectRatio: false,
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)