feature(redux-devtools-inspector-monitor): add option to sort state tree alphabetically and/or disable collections (#1264)

Co-authored-by: Kent Kwee <kent.kwee@tngtech.com>
This commit is contained in:
kentkwee 2023-04-11 14:21:40 +02:00 committed by GitHub
parent c52cfbe469
commit d54adb76f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 150 additions and 0 deletions

View File

@ -0,0 +1,7 @@
---
'remotedev-redux-devtools-extension': minor
'@redux-devtools/inspector-monitor': minor
---
Option to sort State Tree keys alphabetically
Option to disable collapsing of object keys

View File

@ -60,6 +60,7 @@ class Actions extends Component<Props> {
liftedState,
liftedDispatch,
position,
stateTreeSettings,
} = this.props;
const { features } = options;
return (
@ -75,6 +76,7 @@ class Actions extends Component<Props> {
monitorState={this.props.monitorState}
dispatch={liftedDispatch}
features={options.features}
stateTreeSettings={stateTreeSettings}
/>
{sliderIsOpen && options.connectionId && options.features.jump && (
<SliderMonitor liftedState={liftedState} dispatch={liftedDispatch} />
@ -149,6 +151,7 @@ const mapStateToProps = (state: StoreState) => {
dispatcherIsOpen: state.monitor.dispatcherIsOpen,
sliderIsOpen: state.monitor.sliderIsOpen,
reports: state.reports.data,
stateTreeSettings: state.stateTreeSettings,
};
};

View File

@ -7,6 +7,7 @@ import {
section,
socket,
theme,
stateTreeSettings,
StoreState,
} from '@redux-devtools/app';
import instances from './instancesReducer';
@ -22,6 +23,7 @@ const rootReducer: Reducer<StoreState, WindowStoreAction> =
section,
theme,
connection,
stateTreeSettings,
});
export default rootReducer;

View File

@ -25,6 +25,7 @@ import {
GET_REPORT_SUCCESS,
ERROR,
SET_PERSIST,
CHANGE_STATE_TREE_SETTINGS,
} from '../constants/actionTypes';
import {
AUTH_ERROR,
@ -82,6 +83,27 @@ export function changeTheme(data: ChangeThemeData): ChangeThemeAction {
return { type: CHANGE_THEME, ...data.formData };
}
interface ChangeStateTreeSettingsFormData {
readonly sortAlphabetically: boolean;
readonly disableCollection: boolean;
}
interface ChangeStateTreeSettingsData {
readonly formData: ChangeStateTreeSettingsFormData;
}
export interface ChangeStateTreeSettingsAction {
readonly type: typeof CHANGE_STATE_TREE_SETTINGS;
readonly sortAlphabetically: boolean;
readonly disableCollection: boolean;
}
export function changeStateTreeSettings(
data: ChangeStateTreeSettingsData
): ChangeStateTreeSettingsAction {
return { type: CHANGE_STATE_TREE_SETTINGS, ...data.formData };
}
export interface InitMonitorAction {
type: '@@INIT_MONITOR';
newMonitorState: unknown;
@ -568,6 +590,7 @@ interface ReduxPersistRehydrateAction {
export type StoreActionWithoutUpdateStateOrLiftedAction =
| ChangeSectionAction
| ChangeThemeAction
| ChangeStateTreeSettingsAction
| MonitorActionAction
| SelectInstanceAction
| SelectMonitorAction

View File

@ -0,0 +1,52 @@
import React, { Component } from 'react';
import { connect, ResolveThunks } from 'react-redux';
import { Container, Form } from '@redux-devtools/ui';
import { changeStateTreeSettings } from '../../actions';
import { StoreState } from '../../reducers';
type StateProps = ReturnType<typeof mapStateToProps>;
type DispatchProps = ResolveThunks<typeof actionCreators>;
type Props = StateProps & DispatchProps;
export class StateTree extends Component<Props> {
render() {
const stateTree = this.props.theme;
const formData = {
sortAlphabetically: stateTree.sortAlphabetically,
disableCollection: stateTree.disableCollection,
};
return (
<Container>
<Form
schema={{
type: 'object',
properties: {
sortAlphabetically: {
title: 'Sort Alphabetically',
type: 'boolean',
},
disableCollection: {
title: 'Disable collapsing of nodes',
type: 'boolean',
},
},
}}
formData={formData}
noSubmit
onChange={this.props.changeStateTreeSettings}
/>
</Container>
);
}
}
const mapStateToProps = (state: StoreState) => ({
theme: state.stateTreeSettings,
});
const actionCreators = {
changeStateTreeSettings,
};
export default connect(mapStateToProps, actionCreators)(StateTree);

View File

@ -2,6 +2,7 @@ import React, { Component } from 'react';
import { Tabs } from '@redux-devtools/ui';
import Connection from './Connection';
import Themes from './Themes';
import StateTree from './StateTree';
interface State {
selected: string;
@ -12,6 +13,7 @@ export default class Settings extends Component<{}, State> {
tabs = [
{ name: 'Connection', component: Connection },
{ name: 'Themes', component: Themes },
{ name: 'State Tree', component: StateTree },
];
state: State = { selected: 'Connection' };

View File

@ -1,5 +1,6 @@
export const CHANGE_SECTION = 'main/CHANGE_SECTION';
export const CHANGE_THEME = 'main/CHANGE_THEME';
export const CHANGE_STATE_TREE_SETTINGS = 'main/CHANGE_STATE_TREE_SETTINGS';
export const UPDATE_STATE = 'devTools/UPDATE_STATE';
export const SET_STATE = 'devTools/SET_STATE';

View File

@ -23,6 +23,7 @@ class Actions extends Component<Props> {
options,
liftedState,
liftedDispatch,
stateTreeSettings,
} = this.props;
return (
<Container>
@ -37,6 +38,7 @@ class Actions extends Component<Props> {
monitorState={this.props.monitorState}
dispatch={liftedDispatch}
features={options.features}
stateTreeSettings={stateTreeSettings}
/>
{sliderIsOpen && options.connectionId && options.features.jump && (
<SliderMonitor liftedState={liftedState} dispatch={liftedDispatch} />
@ -65,6 +67,7 @@ const mapStateToProps = (state: StoreState) => {
dispatcherIsOpen: state.monitor.dispatcherIsOpen,
sliderIsOpen: state.monitor.sliderIsOpen,
reports: state.reports.data,
stateTreeSettings: state.stateTreeSettings,
};
};

View File

@ -7,6 +7,7 @@ import { InitMonitorAction } from '../actions';
import { Features, State } from '../reducers/instances';
import { MonitorStateMonitorState } from '../reducers/monitor';
import { ThemeFromProvider } from '@redux-devtools/ui';
import { StateTreeSettings } from '../reducers/stateTreeSettings';
interface Props {
monitor: string;
@ -17,6 +18,7 @@ interface Props {
) => void;
features: Features | undefined;
theme: ThemeFromProvider;
stateTreeSettings: StateTreeSettings;
}
class DevTools extends Component<Props> {
@ -110,6 +112,12 @@ class DevTools extends Component<Props> {
features={this.props.features}
dispatch={this.dispatch}
theme={this.props.theme}
sortStateTreeAlphabetically={
this.props.stateTreeSettings.sortAlphabetically
}
disableStateTreeCollection={
this.props.stateTreeSettings.disableCollection
}
/>
</div>
);

View File

@ -65,5 +65,6 @@ export * from './reducers/reports';
export * from './reducers/section';
export * from './reducers/socket';
export * from './reducers/theme';
export * from './reducers/stateTreeSettings';
export * from './utils/monitorActions';
export * from './utils/stringifyJSON';

View File

@ -8,10 +8,12 @@ import { instances, InstancesState } from './instances';
import { reports, ReportsState } from './reports';
import { theme, ThemeState } from './theme';
import { StoreAction } from '../actions';
import { stateTreeSettings, StateTreeSettings } from './stateTreeSettings';
export interface StoreState {
readonly section: SectionState;
readonly theme: ThemeState;
readonly stateTreeSettings: StateTreeSettings;
readonly connection: ConnectionState;
readonly socket: SocketState;
readonly monitor: MonitorState;
@ -23,6 +25,7 @@ export interface StoreState {
export const rootReducer = combineReducers<StoreState, StoreAction>({
section,
theme,
stateTreeSettings,
connection,
socket,
monitor,

View File

@ -0,0 +1,23 @@
import { CHANGE_STATE_TREE_SETTINGS } from '../constants/actionTypes';
import { StoreAction } from '../actions';
export interface StateTreeSettings {
readonly sortAlphabetically: boolean;
readonly disableCollection: boolean;
}
export function stateTreeSettings(
state: StateTreeSettings = {
sortAlphabetically: false,
disableCollection: false,
},
action: StoreAction
) {
if (action.type === CHANGE_STATE_TREE_SETTINGS) {
return {
sortAlphabetically: action.sortAlphabetically,
disableCollection: action.disableCollection,
};
}
return state;
}

View File

@ -21,6 +21,8 @@ export interface TabComponentProps<S, A extends Action<unknown>> {
base16Theme: Base16Theme;
invertTheme: boolean;
isWideLayout: boolean;
sortStateTreeAlphabetically: boolean;
disableStateTreeCollection: boolean;
dataTypeKey: string | symbol | undefined;
delta: Delta | null | undefined | false;
action: A;
@ -70,6 +72,8 @@ interface Props<S, A extends Action<unknown>> {
onInspectPath: (path: (string | number)[]) => void;
inspectedPath: (string | number)[];
onSelectTab: (tabName: string) => void;
sortStateTreeAlphabetically: boolean;
disableStateTreeCollection: boolean;
}
class ActionPreview<S, A extends Action<unknown>> extends Component<
@ -101,6 +105,8 @@ class ActionPreview<S, A extends Action<unknown>> extends Component<
dataTypeKey,
monitorState,
updateMonitorState,
sortStateTreeAlphabetically,
disableStateTreeCollection,
} = this.props;
const renderedTabs: Tab<S, A>[] =
@ -133,6 +139,8 @@ class ActionPreview<S, A extends Action<unknown>> extends Component<
base16Theme,
invertTheme,
isWideLayout,
sortStateTreeAlphabetically,
disableStateTreeCollection,
dataTypeKey,
delta,
action,

View File

@ -151,6 +151,8 @@ export interface ExternalProps<S, A extends Action<unknown>> {
hideMainButtons?: boolean;
hideActionButtons?: boolean;
invertTheme: boolean;
sortStateTreeAlphabetically: boolean;
disableStateTreeCollection: boolean;
dataTypeKey?: string | symbol;
tabs: Tab<S, A>[] | ((tabs: Tab<S, A>[]) => Tab<S, A>[]);
}
@ -177,6 +179,8 @@ export interface DevtoolsInspectorProps<S, A extends Action<unknown>>
diffPropertyFilter?: (name: string, context: DiffContext) => boolean;
hideMainButtons?: boolean;
hideActionButtons?: boolean;
sortStateTreeAlphabetically: boolean;
disableStateTreeCollection: boolean;
invertTheme: boolean;
dataTypeKey?: string | symbol;
tabs: Tab<S, A>[] | ((tabs: Tab<S, A>[]) => Tab<S, A>[]);
@ -220,6 +224,8 @@ class DevtoolsInspector<S, A extends Action<unknown>> extends PureComponent<
hideMainButtons: PropTypes.bool,
hideActionButtons: PropTypes.bool,
invertTheme: PropTypes.bool,
sortStateTreeAlphabetically: PropTypes.bool,
disableStateTreeCollection: PropTypes.bool,
skippedActionIds: PropTypes.array,
dataTypeKey: PropTypes.any,
tabs: PropTypes.oneOfType([PropTypes.array, PropTypes.func]),
@ -305,6 +311,8 @@ class DevtoolsInspector<S, A extends Action<unknown>> extends PureComponent<
dataTypeKey,
hideMainButtons,
hideActionButtons,
sortStateTreeAlphabetically,
disableStateTreeCollection,
} = this.props;
const { selectedActionId, startActionId, searchValue, tabName } =
monitorState;
@ -364,6 +372,8 @@ class DevtoolsInspector<S, A extends Action<unknown>> extends PureComponent<
selectedActionId,
startActionId,
dataTypeKey,
sortStateTreeAlphabetically,
disableStateTreeCollection,
}}
monitorState={this.props.monitorState}
updateMonitorState={this.updateMonitorState}

View File

@ -16,6 +16,8 @@ const StateTab: React.FunctionComponent<
labelRenderer,
dataTypeKey,
isWideLayout,
sortStateTreeAlphabetically,
disableStateTreeCollection,
}) => (
<JSONTree
labelRenderer={labelRenderer}
@ -26,6 +28,8 @@ const StateTab: React.FunctionComponent<
}
invertTheme={invertTheme}
hideRoot
sortObjectKeys={sortStateTreeAlphabetically}
{...(disableStateTreeCollection ? { collectionLimit: 0 } : {})}
/>
);