feat(redux-devtools-chart-monitor): convert to TypeScript (#642)

* chart-monitor

* and that
This commit is contained in:
Nathan Bierema 2020-09-21 21:23:38 -04:00 committed by GitHub
parent 300b60a8b1
commit 761baba0aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 212 additions and 102 deletions

View File

@ -12,7 +12,7 @@ import d3tooltip from 'd3tooltip';
interface InputOptions { interface InputOptions {
// eslint-disable-next-line @typescript-eslint/ban-types // eslint-disable-next-line @typescript-eslint/ban-types
state?: {}; state?: {} | null;
// eslint-disable-next-line @typescript-eslint/ban-types // eslint-disable-next-line @typescript-eslint/ban-types
tree?: NodeWithId | {}; tree?: NodeWithId | {};
@ -50,7 +50,7 @@ interface InputOptions {
interface Options { interface Options {
// eslint-disable-next-line @typescript-eslint/ban-types // eslint-disable-next-line @typescript-eslint/ban-types
state?: {}; state?: {} | null;
// eslint-disable-next-line @typescript-eslint/ban-types // eslint-disable-next-line @typescript-eslint/ban-types
tree?: NodeWithId | {}; tree?: NodeWithId | {};

View File

@ -1,7 +1,8 @@
{ {
"presets": ["@babel/preset-env", "@babel/preset-react"], "presets": [
"plugins": [ "@babel/preset-env",
"@babel/plugin-proposal-class-properties", "@babel/preset-react",
"@babel/plugin-proposal-export-default-from" "@babel/preset-typescript"
] ],
"plugins": ["@babel/plugin-proposal-class-properties"]
} }

View File

@ -0,0 +1 @@
lib

View File

@ -0,0 +1,13 @@
module.exports = {
extends: '../../.eslintrc',
overrides: [
{
files: ['*.ts', '*.tsx'],
extends: '../../eslintrc.ts.react.base.json',
parserOptions: {
tsconfigRootDir: __dirname,
project: ['./tsconfig.json'],
},
},
],
};

View File

@ -2,21 +2,6 @@
"name": "redux-devtools-chart-monitor", "name": "redux-devtools-chart-monitor",
"version": "1.7.2", "version": "1.7.2",
"description": "Chart monitor for Redux DevTools", "description": "Chart monitor for Redux DevTools",
"main": "lib/index.js",
"scripts": {
"clean": "rimraf lib",
"build": "babel src --out-dir lib",
"prepare": "npm run build",
"prepublishOnly": "npm run clean && npm run build"
},
"files": [
"lib",
"src"
],
"repository": {
"type": "git",
"url": "https://github.com/reduxjs/redux-devtools.git"
},
"keywords": [ "keywords": [
"redux", "redux",
"devtools", "devtools",
@ -24,32 +9,52 @@
"react", "react",
"chart" "chart"
], ],
"author": "romseguy", "homepage": "https://github.com/reduxjs/redux-devtools/tree/master/packages/redux-devtools-chart-monitor",
"license": "MIT",
"bugs": { "bugs": {
"url": "https://github.com/reduxjs/redux-devtools/issues" "url": "https://github.com/reduxjs/redux-devtools/issues"
}, },
"homepage": "https://github.com/reduxjs/redux-devtools", "license": "MIT",
"devDependencies": { "author": "romseguy",
"@babel/cli": "^7.10.5", "files": [
"@babel/core": "^7.11.1", "lib",
"@babel/plugin-proposal-class-properties": "^7.10.4", "src"
"@babel/plugin-proposal-export-default-from": "^7.10.4", ],
"@babel/preset-env": "^7.11.0", "main": "lib/index.js",
"@babel/preset-react": "^7.10.4", "types": "lib/index.d.ts",
"babel-loader": "^8.1.0", "repository": {
"rimraf": "^3.0.2" "type": "git",
"url": "https://github.com/reduxjs/redux-devtools.git"
}, },
"peerDependencies": { "scripts": {
"react": "^16.3.0", "build": "npm run build:types && npm run build:js",
"react-dom": "^16.3.0", "build:types": "tsc --emitDeclarationOnly",
"redux-devtools": "^3.0.0" "build:js": "babel src --out-dir lib --extensions \".ts,.tsx\" --source-maps inline",
"clean": "rimraf lib",
"lint": "eslint . --ext .ts,.tsx",
"lint:fix": "eslint . --ext .ts,.tsx --fix",
"type-check": "tsc --noEmit",
"type-check:watch": "npm run type-check -- --watch",
"preversion": "npm run type-check && npm run lint",
"prepublishOnly": "npm run clean && npm run build"
}, },
"dependencies": { "dependencies": {
"@types/prop-types": "^15.7.3",
"@types/redux-devtools-themes": "^1.0.0",
"d3-state-visualizer": "^1.3.4", "d3-state-visualizer": "^1.3.4",
"deepmerge": "^4.2.2", "deepmerge": "^4.2.2",
"prop-types": "^15.7.2", "prop-types": "^15.7.2",
"react-pure-render": "^1.0.2",
"redux-devtools-themes": "^1.0.0" "redux-devtools-themes": "^1.0.0"
},
"devDependencies": {
"@types/react": "^16.9.46",
"react": "^16.13.1",
"redux": "^4.0.5",
"redux-devtools": "^3.7.0"
},
"peerDependencies": {
"@types/react": "^16.3.18",
"react": "^16.3.0",
"redux": "^3.4.0 || ^4.0.0",
"redux-devtools": "^3.0.0"
} }
} }

View File

@ -1,13 +1,44 @@
import React, { Component, createRef } from 'react'; import React, { Component, createRef } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { tree } from 'd3-state-visualizer'; import { tree } from 'd3-state-visualizer';
import { Action, Dispatch } from 'redux';
import { LiftedAction, LiftedState } from 'redux-devtools-instrument';
import * as themes from 'redux-devtools-themes';
import { Base16Theme } from 'react-base16-styling';
import { ChartMonitorState } from './reducers';
import { Primitive } from 'd3';
const wrapperStyle = { const wrapperStyle = {
width: '100%', width: '100%',
height: '100%', height: '100%',
}; };
class Chart extends Component { export interface Props<S, A extends Action<unknown>>
extends LiftedState<S, A, ChartMonitorState> {
dispatch: Dispatch<LiftedAction<S, A, ChartMonitorState>>;
preserveScrollTop: boolean;
select: (state: S) => unknown;
theme: keyof typeof themes | Base16Theme;
invertTheme: boolean;
state: S | null;
isSorted: boolean;
heightBetweenNodesCoeff: number;
widthBetweenNodesCoeff: number;
tooltipOptions: {
disabled: boolean;
offset: {
left: number;
top: number;
};
indentationSize: number;
style: { [key: string]: Primitive } | undefined;
};
style: { [key: string]: Primitive } | undefined;
defaultIsVisible?: boolean;
}
class Chart<S, A extends Action<unknown>> extends Component<Props<S, A>> {
static propTypes = { static propTypes = {
state: PropTypes.object, state: PropTypes.object,
rootKeyName: PropTypes.string, rootKeyName: PropTypes.string,
@ -60,21 +91,25 @@ class Chart extends Component {
}), }),
}; };
divRef = createRef(); divRef = createRef<HTMLDivElement>();
// eslint-disable-next-line @typescript-eslint/ban-types
renderChart?: (state?: {} | null | undefined) => void;
componentDidMount() { componentDidMount() {
const { select, state, defaultIsVisible } = this.props; const { select, state, defaultIsVisible } = this.props;
this.renderChart = tree(this.divRef.current, this.props); this.renderChart = tree(this.divRef.current!, this.props);
if (defaultIsVisible) { if (defaultIsVisible) {
this.renderChart(select(state)); // eslint-disable-next-line @typescript-eslint/ban-types
this.renderChart(select(state!) as {} | null | undefined);
} }
} }
UNSAFE_componentWillReceiveProps(nextProps) { UNSAFE_componentWillReceiveProps(nextProps: Props<S, A>) {
const { state, select, monitorState } = nextProps; const { state, select, monitorState } = nextProps;
if (monitorState.isVisible !== false) { if (monitorState.isVisible !== false) {
this.renderChart(select(state)); // eslint-disable-next-line @typescript-eslint/ban-types
this.renderChart!(select(state!) as {} | null | undefined);
} }
} }

View File

@ -1,15 +1,18 @@
import React, { Component } from 'react'; import React, { CSSProperties, PureComponent } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import shouldPureComponentUpdate from 'react-pure-render/function';
import * as themes from 'redux-devtools-themes'; import * as themes from 'redux-devtools-themes';
import { ActionCreators } from 'redux-devtools'; import { ActionCreators, LiftedAction, LiftedState } from 'redux-devtools';
import deepmerge from 'deepmerge'; import deepmerge from 'deepmerge';
import { Action, Dispatch } from 'redux';
import { Base16Theme } from 'react-base16-styling';
import reducer from './reducers'; import reducer, { ChartMonitorState } from './reducers';
import Chart from './Chart'; import Chart, { Props } from './Chart';
import { Primitive } from 'd3';
// eslint-disable-next-line @typescript-eslint/unbound-method
const { reset, rollback, commit, sweep, toggleAction } = ActionCreators; const { reset, rollback, commit, sweep, toggleAction } = ActionCreators;
const styles = { const styles: { container: CSSProperties } = {
container: { container: {
fontFamily: 'monaco, Consolas, Lucida Console, monospace', fontFamily: 'monaco, Consolas, Lucida Console, monospace',
position: 'relative', position: 'relative',
@ -20,7 +23,7 @@ const styles = {
}, },
}; };
function invertColors(theme) { function invertColors(theme: Base16Theme) {
return { return {
...theme, ...theme,
base00: theme.base07, base00: theme.base07,
@ -34,7 +37,43 @@ function invertColors(theme) {
}; };
} }
class ChartMonitor extends Component { export interface ChartMonitorProps<S, A extends Action<unknown>>
extends LiftedState<S, A, ChartMonitorState> {
dispatch: Dispatch<LiftedAction<S, A, ChartMonitorState>>;
preserveScrollTop: boolean;
select: (state: S) => unknown;
theme: keyof typeof themes | Base16Theme;
invertTheme: boolean;
state: S | null;
isSorted: boolean;
heightBetweenNodesCoeff: number;
widthBetweenNodesCoeff: number;
tooltipOptions: unknown;
style: {
width: number;
height: number;
node: {
colors: {
default: string;
collapsed: string;
parent: string;
};
radius: number;
};
text: {
colors: {
default: string;
hover: string;
};
};
};
defaultIsVisible?: boolean;
}
class ChartMonitor<S, A extends Action<unknown>> extends PureComponent<
ChartMonitorProps<S, A>
> {
static update = reducer; static update = reducer;
static propTypes = { static propTypes = {
@ -55,45 +94,34 @@ class ChartMonitor extends Component {
}; };
static defaultProps = { static defaultProps = {
select: (state) => state, select: (state: unknown) => state,
theme: 'nicinabox', theme: 'nicinabox',
preserveScrollTop: true, preserveScrollTop: true,
invertTheme: false, invertTheme: false,
}; };
shouldComponentUpdate = shouldPureComponentUpdate; handleRollback = () => {
constructor(props) {
super(props);
this.handleToggleAction = this.handleToggleAction.bind(this);
this.handleReset = this.handleReset.bind(this);
this.handleRollback = this.handleRollback.bind(this);
this.handleSweep = this.handleSweep.bind(this);
this.handleCommit = this.handleCommit.bind(this);
}
handleRollback() {
this.props.dispatch(rollback()); this.props.dispatch(rollback());
} };
handleSweep() { handleSweep = () => {
this.props.dispatch(sweep()); this.props.dispatch(sweep());
} };
handleCommit() { handleCommit = () => {
this.props.dispatch(commit()); this.props.dispatch(commit());
} };
handleToggleAction(id) { handleToggleAction = (id: number) => {
this.props.dispatch(toggleAction(id)); this.props.dispatch(toggleAction(id));
} };
handleReset() { handleReset = () => {
this.props.dispatch(reset()); this.props.dispatch(reset());
} };
getTheme() { getTheme() {
let { theme, invertTheme } = this.props; const { theme, invertTheme } = this.props;
if (typeof theme !== 'string') { if (typeof theme !== 'string') {
return invertTheme ? invertColors(theme) : theme; return invertTheme ? invertColors(theme) : theme;
} }
@ -102,7 +130,6 @@ class ChartMonitor extends Component {
return invertTheme ? invertColors(themes[theme]) : themes[theme]; return invertTheme ? invertColors(themes[theme]) : themes[theme];
} }
// eslint-disable-next-line no-console
console.warn( console.warn(
'DevTools theme ' + theme + ' not found, defaulting to nicinabox' 'DevTools theme ' + theme + ' not found, defaulting to nicinabox'
); );
@ -132,7 +159,7 @@ class ChartMonitor extends Component {
}; };
} }
getChartOptions(props = this.props) { getChartOptions(props = this.props): Props<S, A> {
const { computedStates } = props; const { computedStates } = props;
const theme = this.getTheme(); const theme = this.getTheme();
@ -156,7 +183,9 @@ class ChartMonitor extends Component {
heightBetweenNodesCoeff: 1, heightBetweenNodesCoeff: 1,
widthBetweenNodesCoeff: 1.3, widthBetweenNodesCoeff: 1.3,
tooltipOptions, tooltipOptions,
style: this.getChartStyle(), style: (this.getChartStyle() as unknown) as
| { [key: string]: Primitive }
| undefined,
}; };
return deepmerge(defaultOptions, props); return deepmerge(defaultOptions, props);
@ -165,9 +194,10 @@ class ChartMonitor extends Component {
render() { render() {
const theme = this.getTheme(); const theme = this.getTheme();
const ChartAsAny = Chart as any;
return ( return (
<div style={{ ...styles.container, backgroundColor: theme.base07 }}> <div style={{ ...styles.container, backgroundColor: theme.base07 }}>
<Chart {...this.getChartOptions()} /> <ChartAsAny {...this.getChartOptions()} />
</div> </div>
); );
} }

View File

@ -1,2 +0,0 @@
export const TOGGLE_VISIBILITY =
'@@redux-devtools-log-monitor/TOGGLE_VISIBILITY';

View File

@ -0,0 +1,6 @@
export const TOGGLE_VISIBILITY =
'@@redux-devtools-log-monitor/TOGGLE_VISIBILITY';
interface ToggleVisibilityAction {
type: typeof TOGGLE_VISIBILITY;
}
export type ChartMonitorAction = ToggleVisibilityAction;

View File

@ -1 +0,0 @@
export default from './ChartMonitor';

View File

@ -0,0 +1 @@
export { default } from './ChartMonitor';

View File

@ -1,19 +0,0 @@
import { TOGGLE_VISIBILITY } from './actions';
function toggleVisibility(props, state = props.defaultIsVisible, action) {
if (action.type === TOGGLE_VISIBILITY) {
return !state;
}
if (props.defaultIsVisible !== undefined) {
return props.defaultIsVisible;
}
return true;
}
export default function reducer(props, state = {}, action) {
return {
isVisible: toggleVisibility(props, state.isVisible, action),
};
}

View File

@ -0,0 +1,33 @@
import { Action } from 'redux';
import { ChartMonitorAction, TOGGLE_VISIBILITY } from './actions';
import { ChartMonitorProps } from './ChartMonitor';
function toggleVisibility<S, A extends Action<unknown>>(
props: ChartMonitorProps<S, A>,
state = props.defaultIsVisible,
action: ChartMonitorAction
): boolean {
if (action.type === TOGGLE_VISIBILITY) {
return !state;
}
if (props.defaultIsVisible !== undefined) {
return props.defaultIsVisible;
}
return true;
}
export interface ChartMonitorState {
isVisible?: boolean;
}
export default function reducer<S, A extends Action<unknown>>(
props: ChartMonitorProps<S, A>,
state: ChartMonitorState | undefined = {},
action: ChartMonitorAction
) {
return {
isVisible: toggleVisibility(props, state.isVisible, action),
};
}

View File

@ -0,0 +1,7 @@
{
"extends": "../../tsconfig.react.base.json",
"compilerOptions": {
"outDir": "lib"
},
"include": ["src"]
}