Skip to content

Commit eb42ce8

Browse files
authored
Merge pull request #368 from plotly/cam/switch-prop-types-to-ts
build: Switch prop-types to TS
2 parents 6781433 + 439b54b commit eb42ce8

9 files changed

Lines changed: 172 additions & 46 deletions

File tree

.github/workflows/test.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ on:
88
jobs:
99
test:
1010
runs-on: ubuntu-latest
11+
12+
strategy:
13+
fail-fast: false
14+
matrix:
15+
react-version: [18, 19]
16+
1117
steps:
1218
- uses: actions/checkout@v4
1319

@@ -19,11 +25,14 @@ jobs:
1925
- name: Install dependencies
2026
run: npm install
2127

28+
- name: Install React ${{ matrix.react-version }}
29+
run: npm install --no-save react@${{ matrix.react-version }} react-dom@${{ matrix.react-version }}
30+
2231
- name: List dependency versions
2332
run: |
2433
echo "npm: $(npm --version)"
2534
echo "node: $(node --version)"
26-
npm ls || true
35+
npm ls react react-dom || true
2736
2837
- name: Run tests
2938
run: npm run test

eslint.config.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ export default [
107107
'react/no-is-mounted': ['error'],
108108
'react/no-unknown-property': ['error'],
109109
'react/prefer-es6-class': ['error', 'always'],
110-
'react/prop-types': 'error',
110+
'react/prop-types': 'off',
111111
yoda: ['error'],
112112
'spaced-comment': [
113113
'error',

package-lock.json

Lines changed: 35 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,29 @@
1111
},
1212
"main": "./dist/index.cjs",
1313
"module": "./dist/index.mjs",
14+
"types": "./dist/index.d.ts",
1415
"unpkg": "./dist/create-plotly-component.min.js",
1516
"jsdelivr": "./dist/create-plotly-component.min.js",
1617
"exports": {
1718
".": {
18-
"import": "./dist/index.mjs",
19-
"require": "./dist/index.cjs"
19+
"import": {
20+
"types": "./dist/index.d.mts",
21+
"default": "./dist/index.mjs"
22+
},
23+
"require": {
24+
"types": "./dist/index.d.ts",
25+
"default": "./dist/index.cjs"
26+
}
2027
},
2128
"./factory": {
22-
"import": "./dist/factory.mjs",
23-
"require": "./dist/factory.cjs"
29+
"import": {
30+
"types": "./dist/factory.d.mts",
31+
"default": "./dist/factory.mjs"
32+
},
33+
"require": {
34+
"types": "./dist/factory.d.ts",
35+
"default": "./dist/factory.cjs"
36+
}
2437
},
2538
"./package.json": "./package.json"
2639
},
@@ -38,8 +51,9 @@
3851
"build": "tsup",
3952
"clean": "rimraf dist",
4053
"prepublishOnly": "npm run build",
41-
"lint": "prettier --trailing-comma es5 --write \"src/**/*.js\" && eslint src",
42-
"test": "npm run lint && jest",
54+
"lint": "prettier --write \"src/**/*.js\" && eslint src",
55+
"typecheck": "tsc",
56+
"test": "npm run lint && npm run typecheck && jest",
4357
"watch-test": "jest --watch",
4458
"watch": "tsup --watch"
4559
},
@@ -51,16 +65,14 @@
5165
"plotly",
5266
"react"
5367
],
54-
"dependencies": {
55-
"prop-types": "^15.8.1"
56-
},
5768
"devDependencies": {
5869
"@babel/core": "^7.19.0",
5970
"@babel/eslint-parser": "^7.29.0",
6071
"@babel/preset-env": "^7.19.0",
6172
"@babel/preset-react": "^7.18.6",
6273
"@eslint/js": "^9.39.0",
6374
"@testing-library/react": "^16.0.0",
75+
"@types/react": "^19.2.17",
6476
"eslint": "^9.39.0",
6577
"eslint-config-prettier": "^10.1.0",
6678
"eslint-plugin-import": "^2.32.0",

src/factory.d.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import type React from 'react';
2+
3+
/**
4+
* The snapshot of plot state passed to figure callbacks.
5+
* `data`, `layout`, and the entries of `frames` are plotly.js objects; this
6+
* package does not depend on `@types/plotly.js`, so they are typed as
7+
* `unknown`. Consumers wanting tighter types can re-declare with imports
8+
* from `plotly.js`.
9+
*/
10+
export interface Figure {
11+
data: unknown[];
12+
layout: unknown;
13+
frames: unknown[] | null;
14+
}
15+
16+
export type FigureCallback = (figure: Figure, graphDiv: HTMLElement) => void;
17+
export type EventCallback = (event: any) => void;
18+
19+
export interface PlotParams {
20+
data?: unknown[];
21+
layout?: unknown;
22+
config?: unknown;
23+
frames?: unknown[];
24+
revision?: number;
25+
onInitialized?: FigureCallback;
26+
onUpdate?: FigureCallback;
27+
onPurge?: FigureCallback;
28+
onError?: (err: Error) => void;
29+
debug?: boolean;
30+
style?: React.CSSProperties;
31+
className?: string;
32+
useResizeHandler?: boolean;
33+
divId?: string;
34+
35+
onAfterExport?: EventCallback;
36+
onAfterPlot?: EventCallback;
37+
onAnimated?: EventCallback;
38+
onAnimatingFrame?: EventCallback;
39+
onAnimationInterrupted?: EventCallback;
40+
onAutoSize?: EventCallback;
41+
onBeforeExport?: EventCallback;
42+
onBeforeHover?: EventCallback;
43+
onButtonClicked?: EventCallback;
44+
onClick?: EventCallback;
45+
onClickAnnotation?: EventCallback;
46+
onClickAnywhere?: EventCallback;
47+
onDeselect?: EventCallback;
48+
onDoubleClick?: EventCallback;
49+
onFramework?: EventCallback;
50+
onHover?: EventCallback;
51+
onHoverAnywhere?: EventCallback;
52+
onLegendClick?: EventCallback;
53+
onLegendDoubleClick?: EventCallback;
54+
onRelayout?: EventCallback;
55+
onRelayouting?: EventCallback;
56+
onRestyle?: EventCallback;
57+
onRedraw?: EventCallback;
58+
onSelected?: EventCallback;
59+
onSelecting?: EventCallback;
60+
onSliderChange?: EventCallback;
61+
onSliderEnd?: EventCallback;
62+
onSliderStart?: EventCallback;
63+
onSunburstClick?: EventCallback;
64+
onTransitioning?: EventCallback;
65+
onTransitionInterrupted?: EventCallback;
66+
onUnhover?: EventCallback;
67+
onWebGlContextLost?: EventCallback;
68+
}
69+
70+
/**
71+
* Build a Plot component bound to a specific plotly.js instance. Use this
72+
* when shipping a custom plotly.js bundle (e.g. the basic, cartesian, or
73+
* a custom partial bundle) instead of the full library.
74+
*/
75+
declare function createPlotlyComponent(Plotly: unknown): React.ComponentType<PlotParams>;
76+
77+
export default createPlotlyComponent;

src/factory.js

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import React, {Component} from 'react';
2-
import PropTypes from 'prop-types';
32

43
// The naming convention is:
54
// - events are attached as `'plotly_' + eventName.toLowerCase()`
@@ -257,27 +256,6 @@ export default function plotComponentFactory(Plotly) {
257256
}
258257
}
259258

260-
PlotlyComponent.propTypes = {
261-
data: PropTypes.arrayOf(PropTypes.object),
262-
config: PropTypes.object,
263-
layout: PropTypes.object,
264-
frames: PropTypes.arrayOf(PropTypes.object),
265-
revision: PropTypes.number,
266-
onInitialized: PropTypes.func,
267-
onPurge: PropTypes.func,
268-
onError: PropTypes.func,
269-
onUpdate: PropTypes.func,
270-
debug: PropTypes.bool,
271-
style: PropTypes.object,
272-
className: PropTypes.string,
273-
useResizeHandler: PropTypes.bool,
274-
divId: PropTypes.string,
275-
};
276-
277-
eventNames.forEach((eventName) => {
278-
PlotlyComponent.propTypes['on' + eventName] = PropTypes.func;
279-
});
280-
281259
PlotlyComponent.defaultProps = {
282260
debug: false,
283261
useResizeHandler: false,

src/index.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import type React from 'react';
2+
import type {PlotParams} from './factory';
3+
4+
declare const Plot: React.ComponentType<PlotParams>;
5+
6+
export default Plot;
7+
export {Figure, FigureCallback, EventCallback, PlotParams} from './factory';

tsconfig.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"include": ["src/**/*.d.ts"],
3+
"compilerOptions": {
4+
"noEmit": true,
5+
"strict": true,
6+
"jsx": "preserve",
7+
"esModuleInterop": true,
8+
"moduleResolution": "bundler",
9+
"target": "es2022"
10+
}
11+
}

tsup.config.mjs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import {copyFile} from 'node:fs/promises';
12
import {defineConfig} from 'tsup';
23

34
// Mirror browserify-global-shim: rewrite `import React from 'react'` as
@@ -31,9 +32,17 @@ export default defineConfig([
3132
},
3233
sourcemap: true,
3334
clean: true,
34-
external: ['react', 'plotly.js', 'prop-types'],
35+
external: ['react', 'plotly.js'],
3536
outDir: 'dist',
3637
loader: {'.js': 'jsx'},
38+
onSuccess: async () => {
39+
// Ship the same declarations under both .d.ts (CJS interp) and .d.mts (ESM interp)
40+
// so the exports map's per-condition types entries resolve unambiguously.
41+
for (const name of ['index', 'factory']) {
42+
await copyFile(`src/${name}.d.ts`, `dist/${name}.d.ts`);
43+
await copyFile(`src/${name}.d.ts`, `dist/${name}.d.mts`);
44+
}
45+
},
3746
},
3847
// UMD / IIFE for <script>-tag consumers. React is taken from window.React
3948
// via the plugin above; plotly.js still external (consumer loads it the

0 commit comments

Comments
 (0)